Merge "Bluetooth: Test App for MAP, PBAP and HFP client role."
diff --git a/Android.mk b/Android.mk
index 4e4a62c..bbd17ec 100644
--- a/Android.mk
+++ b/Android.mk
@@ -23,34 +23,5 @@
 
 include $(BUILD_PACKAGE)
 
-include $(CLEAR_VARS)
-
-src_dirs:=  src/org/codeaurora/bluetooth/mapclient
-
-LOCAL_SRC_FILES := \
-        $(call all-java-files-under, $(src_dirs))
-
-LOCAL_MODULE:= org.codeaurora.bluetooth.mapclient
-LOCAL_JAVA_LIBRARIES := javax.obex
-LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-include $(CLEAR_VARS)
-
-src_dirs:=  src/org/codeaurora/bluetooth/pbapclient
-
-LOCAL_SRC_FILES := \
-        $(call all-java-files-under, $(src_dirs))
-
-LOCAL_MODULE:= org.codeaurora.bluetooth.pbapclient
-LOCAL_JAVA_LIBRARIES := javax.obex
-LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/bttestapp/Android.mk b/bttestapp/Android.mk
new file mode 100644
index 0000000..f560163
--- /dev/null
+++ b/bttestapp/Android.mk
@@ -0,0 +1,50 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+        $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := BTTestApp
+LOCAL_CERTIFICATE := platform
+
+LOCAL_MODULE_OWNER := qcom
+
+LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard org.codeaurora.bluetooth.mapclient org.codeaurora.bluetooth.pbapclient
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+
+src_dirs:=  ../src/org/codeaurora/bluetooth/mapclient
+
+LOCAL_SRC_FILES := \
+        $(call all-java-files-under, $(src_dirs))
+
+LOCAL_MODULE:= org.codeaurora.bluetooth.mapclient
+LOCAL_JAVA_LIBRARIES := javax.obex
+LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+
+src_dirs:=  ../src/org/codeaurora/bluetooth/pbapclient
+
+LOCAL_SRC_FILES := \
+        $(call all-java-files-under, $(src_dirs))
+
+LOCAL_MODULE:= org.codeaurora.bluetooth.pbapclient
+LOCAL_JAVA_LIBRARIES := javax.obex
+LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/bttestapp/AndroidManifest.xml b/bttestapp/AndroidManifest.xml
new file mode 100644
index 0000000..f96e4a5
--- /dev/null
+++ b/bttestapp/AndroidManifest.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="org.codeaurora.bluetooth.bttestapp"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BLUETOOTH_STACK" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
+    <uses-sdk
+        android:minSdkVersion="17"
+        android:targetSdkVersion="17" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme" >
+
+        <uses-library android:name="javax.obex" />
+
+        <activity android:name=".MainActivity" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".VcardFilterActivity" />
+        <!--activity
+            android:name=".HfpTestActivity"
+            android:launchMode="singleTop" /-->
+        <activity
+            android:name=".PbapTestActivity"
+            android:launchMode="singleTop" />
+        <activity
+            android:name=".MapTestActivity"
+            android:launchMode="singleTop" />
+        <activity android:name=".MessageFilterActivity" />
+
+        <service android:name=".ProfileService" />
+<!--
+        <service android:name=".mapclient.BluetoothMnsService">
+            <intent-filter>
+                <action android:name="org.codeaurora.bluetooth.mapclient.BluetoothMnsService" />
+            </intent-filter>
+        </service>
+-->
+        <activity android:name=".services.PbapAuthActivity"
+            android:excludeFromRecents="true"
+            android:theme="@*android:style/Theme.Holo.Dialog.Alert">
+            <intent-filter>
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <receiver android:name=".BluetoothConnectionReceiver" >
+            <intent-filter>
+                <action android:name="org.codeaurora.bluetooth.action.NEW_BLUETOOTH_DEVICE" />
+                <action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
+            </intent-filter>
+        </receiver>
+
+    </application>
+
+</manifest>
diff --git a/bttestapp/res/drawable-hdpi/ic_bt_connected.png b/bttestapp/res/drawable-hdpi/ic_bt_connected.png
new file mode 100644
index 0000000..94c0ffc
--- /dev/null
+++ b/bttestapp/res/drawable-hdpi/ic_bt_connected.png
Binary files differ
diff --git a/bttestapp/res/drawable-hdpi/ic_bt_disconnected.png b/bttestapp/res/drawable-hdpi/ic_bt_disconnected.png
new file mode 100644
index 0000000..e7689b3
--- /dev/null
+++ b/bttestapp/res/drawable-hdpi/ic_bt_disconnected.png
Binary files differ
diff --git a/bttestapp/res/drawable-hdpi/ic_call_incoming_holo_dark.png b/bttestapp/res/drawable-hdpi/ic_call_incoming_holo_dark.png
new file mode 100644
index 0000000..aaa28d0
--- /dev/null
+++ b/bttestapp/res/drawable-hdpi/ic_call_incoming_holo_dark.png
Binary files differ
diff --git a/bttestapp/res/drawable-hdpi/ic_call_outgoing_holo_dark.png b/bttestapp/res/drawable-hdpi/ic_call_outgoing_holo_dark.png
new file mode 100644
index 0000000..f52677a
--- /dev/null
+++ b/bttestapp/res/drawable-hdpi/ic_call_outgoing_holo_dark.png
Binary files differ
diff --git a/bttestapp/res/drawable-hdpi/ic_contact_picture.png b/bttestapp/res/drawable-hdpi/ic_contact_picture.png
new file mode 100644
index 0000000..0997e31
--- /dev/null
+++ b/bttestapp/res/drawable-hdpi/ic_contact_picture.png
Binary files differ
diff --git a/bttestapp/res/drawable-hdpi/ic_launcher.png b/bttestapp/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..f83649f
--- /dev/null
+++ b/bttestapp/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/bttestapp/res/drawable-hdpi/ic_menu_save.png b/bttestapp/res/drawable-hdpi/ic_menu_save.png
new file mode 100644
index 0000000..306d55a
--- /dev/null
+++ b/bttestapp/res/drawable-hdpi/ic_menu_save.png
Binary files differ
diff --git a/bttestapp/res/drawable-hdpi/sym_keyboard_delete.png b/bttestapp/res/drawable-hdpi/sym_keyboard_delete.png
new file mode 100644
index 0000000..77cdf75
--- /dev/null
+++ b/bttestapp/res/drawable-hdpi/sym_keyboard_delete.png
Binary files differ
diff --git a/bttestapp/res/drawable-mdpi/ic_bt_connected.png b/bttestapp/res/drawable-mdpi/ic_bt_connected.png
new file mode 100644
index 0000000..da98b1b
--- /dev/null
+++ b/bttestapp/res/drawable-mdpi/ic_bt_connected.png
Binary files differ
diff --git a/bttestapp/res/drawable-mdpi/ic_bt_disconnected.png b/bttestapp/res/drawable-mdpi/ic_bt_disconnected.png
new file mode 100644
index 0000000..c8eeeee
--- /dev/null
+++ b/bttestapp/res/drawable-mdpi/ic_bt_disconnected.png
Binary files differ
diff --git a/bttestapp/res/drawable-mdpi/ic_call_incoming_holo_dark.png b/bttestapp/res/drawable-mdpi/ic_call_incoming_holo_dark.png
new file mode 100644
index 0000000..98678c9
--- /dev/null
+++ b/bttestapp/res/drawable-mdpi/ic_call_incoming_holo_dark.png
Binary files differ
diff --git a/bttestapp/res/drawable-mdpi/ic_call_outgoing_holo_dark.png b/bttestapp/res/drawable-mdpi/ic_call_outgoing_holo_dark.png
new file mode 100644
index 0000000..bbe177c
--- /dev/null
+++ b/bttestapp/res/drawable-mdpi/ic_call_outgoing_holo_dark.png
Binary files differ
diff --git a/bttestapp/res/drawable-mdpi/ic_contact_picture.png b/bttestapp/res/drawable-mdpi/ic_contact_picture.png
new file mode 100644
index 0000000..b1f8be1
--- /dev/null
+++ b/bttestapp/res/drawable-mdpi/ic_contact_picture.png
Binary files differ
diff --git a/bttestapp/res/drawable-mdpi/ic_launcher.png b/bttestapp/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..8c4edba
--- /dev/null
+++ b/bttestapp/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/bttestapp/res/drawable-mdpi/ic_menu_save.png b/bttestapp/res/drawable-mdpi/ic_menu_save.png
new file mode 100644
index 0000000..ad07695
--- /dev/null
+++ b/bttestapp/res/drawable-mdpi/ic_menu_save.png
Binary files differ
diff --git a/bttestapp/res/drawable-mdpi/sym_keyboard_delete.png b/bttestapp/res/drawable-mdpi/sym_keyboard_delete.png
new file mode 100644
index 0000000..761e7ba
--- /dev/null
+++ b/bttestapp/res/drawable-mdpi/sym_keyboard_delete.png
Binary files differ
diff --git a/bttestapp/res/drawable-xhdpi/ic_bt_connected.png b/bttestapp/res/drawable-xhdpi/ic_bt_connected.png
new file mode 100644
index 0000000..22f1f5f
--- /dev/null
+++ b/bttestapp/res/drawable-xhdpi/ic_bt_connected.png
Binary files differ
diff --git a/bttestapp/res/drawable-xhdpi/ic_bt_disconnected.png b/bttestapp/res/drawable-xhdpi/ic_bt_disconnected.png
new file mode 100644
index 0000000..569f2ba
--- /dev/null
+++ b/bttestapp/res/drawable-xhdpi/ic_bt_disconnected.png
Binary files differ
diff --git a/bttestapp/res/drawable-xhdpi/ic_call_incoming_holo_dark.png b/bttestapp/res/drawable-xhdpi/ic_call_incoming_holo_dark.png
new file mode 100644
index 0000000..d787229
--- /dev/null
+++ b/bttestapp/res/drawable-xhdpi/ic_call_incoming_holo_dark.png
Binary files differ
diff --git a/bttestapp/res/drawable-xhdpi/ic_call_outgoing_holo_dark.png b/bttestapp/res/drawable-xhdpi/ic_call_outgoing_holo_dark.png
new file mode 100644
index 0000000..ada5cf8
--- /dev/null
+++ b/bttestapp/res/drawable-xhdpi/ic_call_outgoing_holo_dark.png
Binary files differ
diff --git a/bttestapp/res/drawable-xhdpi/ic_contact_picture.png b/bttestapp/res/drawable-xhdpi/ic_contact_picture.png
new file mode 100644
index 0000000..46ef148
--- /dev/null
+++ b/bttestapp/res/drawable-xhdpi/ic_contact_picture.png
Binary files differ
diff --git a/bttestapp/res/drawable-xhdpi/ic_launcher.png b/bttestapp/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..b4d5d6c
--- /dev/null
+++ b/bttestapp/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/bttestapp/res/drawable-xhdpi/ic_menu_save.png b/bttestapp/res/drawable-xhdpi/ic_menu_save.png
new file mode 100644
index 0000000..2720602
--- /dev/null
+++ b/bttestapp/res/drawable-xhdpi/ic_menu_save.png
Binary files differ
diff --git a/bttestapp/res/drawable-xhdpi/sym_keyboard_delete.png b/bttestapp/res/drawable-xhdpi/sym_keyboard_delete.png
new file mode 100644
index 0000000..384315e
--- /dev/null
+++ b/bttestapp/res/drawable-xhdpi/sym_keyboard_delete.png
Binary files differ
diff --git a/bttestapp/res/drawable/selected_call_bg.xml b/bttestapp/res/drawable/selected_call_bg.xml
new file mode 100644
index 0000000..bd0d805
--- /dev/null
+++ b/bttestapp/res/drawable/selected_call_bg.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+
+    <solid android:color="@color/call_selected" />
+    <stroke android:width="3dip" android:color="#800000" />
+
+</shape>
diff --git a/bttestapp/res/layout/activity_hfp_test.xml b/bttestapp/res/layout/activity_hfp_test.xml
new file mode 100644
index 0000000..5c3071a
--- /dev/null
+++ b/bttestapp/res/layout/activity_hfp_test.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:layout_margin="5dip"
+    android:baselineAligned="false">
+
+    <LinearLayout
+        android:id="@+id/hfptest_left_panel"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="35"
+        android:orientation="vertical"
+        android:paddingRight="10dip">
+
+        <fragment class="org.codeaurora.bluetooth.bttestapp.IndicatorsFragment"
+            android:id="@+id/indicators"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1" />
+
+        <fragment class="org.codeaurora.bluetooth.bttestapp.DialpadFragment"
+            android:id="@+id/dialpad"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+    </LinearLayout>
+
+    <fragment class="org.codeaurora.bluetooth.bttestapp.CallsListFragment"
+        android:id="@+id/calls_list"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="65" />
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/activity_main.xml b/bttestapp/res/layout/activity_main.xml
new file mode 100644
index 0000000..edfdd60
--- /dev/null
+++ b/bttestapp/res/layout/activity_main.xml
@@ -0,0 +1,107 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    tools:context=".MainActivity" >
+
+    <RelativeLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="16dp" >
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_alignParentLeft="true" >
+
+            <TextView
+                android:id="@+id/device_name"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/blank"
+                android:textAppearance="?android:attr/textAppearanceLarge" />
+
+            <TextView
+                android:id="@+id/device_address"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/blank"
+                android:textAppearance="?android:attr/textAppearanceSmall" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:gravity="center"
+            android:layout_alignParentRight="true" >
+
+            <Button
+                android:id="@+id/discover_services"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="@string/discover_services"
+                android:onClick="onButtonClick" />
+
+            <Button
+                android:id="@+id/select_device"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="@string/select_device"
+                android:onClick="onButtonClick" />
+
+        </LinearLayout>
+
+    </RelativeLayout>
+
+    <TextView
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:text="@string/services_header"
+            style="?android:attr/listSeparatorTextViewStyle" />
+
+    <fragment class="org.codeaurora.bluetooth.bttestapp.ServicesFragment"
+            android:id="@+id/services_list"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:padding="16dp" />
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/activity_map_test.xml b/bttestapp/res/layout/activity_map_test.xml
new file mode 100644
index 0000000..351a860
--- /dev/null
+++ b/bttestapp/res/layout/activity_map_test.xml
@@ -0,0 +1,682 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:background="#101010" >
+
+<LinearLayout
+    android:id="@+id/maptest_folder_nav"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="#003000"
+    style="@style/somePadding" >
+
+    <Button
+        android:id="@+id/maptest_nav_root"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="0"
+        android:text="@string/map_folder_root"
+        android:onClick="onClickSetPathRoot" />
+
+    <Button
+        android:id="@+id/maptest_nav_up"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="0"
+        android:text="@string/map_folder_up"
+        android:onClick="onClickSetPathUp" />
+
+    <TextView
+        android:id="@+id/maptest_nav_current"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:layout_marginLeft="20dp"
+        android:layout_marginRight="10dp"
+        android:gravity="center_vertical|right"
+        android:text="@string/blank" />
+
+    <Spinner
+        android:id="@+id/maptest_nav_folders"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_weight="1" />
+
+    <Button
+        android:id="@+id/maptest_nav_down"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_weight="0"
+        android:text="@string/map_folder_enter"
+        android:onClick="onClickSetPathEnter" />
+
+    <EditText
+        android:layout_marginLeft="40dp"
+        android:id="@+id/maptest_nav_list_max"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="0"
+        android:ems="2"
+        android:inputType="number" />
+
+    <EditText
+        android:id="@+id/maptest_nav_list_offset"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="0"
+        android:ems="2"
+        android:inputType="number" />
+
+    <Button
+        android:id="@+id/maptest_nav_list_get"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_weight="0"
+        android:text="@string/map_folder_listing"
+        android:onClick="onClickGetFolderListing" />
+
+    <Button
+        android:id="@+id/maptest_nav_list_size"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_weight="0"
+        android:text="@string/map_folder_listing_size"
+        android:onClick="onClickGetFolderListingSize" />
+
+</LinearLayout>
+
+<ViewFlipper
+    android:id="@+id/maptest_viewflipper"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+
+<!-- LIST TAB -->
+<LinearLayout
+    android:id="@+id/maptest_tab_list"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal" >
+
+    <!-- Browsing Layout -->
+    <LinearLayout
+        android:layout_width="250dp"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:background="#000030" >
+
+        <!-- Horizontal line -->
+        <View
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            android:layout_weight="1" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal" >
+
+            <Button
+                android:id="@+id/maptest_msglist_params"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="@string/map_message_parameters"
+                android:onClick="onClickMessageParameters" />
+
+            <Button
+                android:id="@+id/maptest_msglist_filter"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="@string/map_message_filter"
+                android:onClick="onClickGetMessagesFilter" />
+
+        </LinearLayout>
+
+        <TableLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:focusableInTouchMode="true" >
+
+            <!-- MaxListCount -->
+            <TableRow>
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/map_max_list_count_prompt" />
+
+                <EditText
+                    android:id="@+id/maptest_msglist_max"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    android:inputType="number" />
+            </TableRow>
+
+            <!-- ListStartOffset -->
+            <TableRow>
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/map_list_start_offset_prompt" />
+
+                <EditText
+                    android:id="@+id/maptest_msglist_offset"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    android:inputType="number" />
+            </TableRow>
+
+            <!-- SubjectLength -->
+            <TableRow>
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/map_subject_length_prompt" />
+
+                <EditText
+                    android:id="@+id/maptest_msglist_subject_len"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    android:inputType="number" />
+            </TableRow>
+
+        </TableLayout>
+
+        <Button
+            android:id="@+id/maptest_msglist_get"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/map_message_listing"
+            android:onClick="onClickGetMessagesListing" />
+
+        <Button
+            android:id="@+id/maptest_msglist_get_size"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/map_message_listing_size"
+            android:onClick="onClickGetMessageListingSize" />
+
+    </LinearLayout>
+
+    <!-- Messages layout -->
+    <ListView
+        android:id="@+id/maptest_msglist_lv"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_weight="1"
+        android:orientation="vertical" >
+
+        <ProgressBar
+            android:id="@+id/maptest_msglist_progressbar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="?android:attr/progressBarStyleLarge"
+            android:visibility="gone" />
+
+        <TextView
+            android:id="@+id/maptest_msglist_empty"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_messages_list_empty"
+            android:layout_gravity="center_horizontal|center_vertical"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</LinearLayout>
+
+<!-- PREVIEW TAB -->
+<LinearLayout
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:focusableInTouchMode="true"
+    android:orientation="vertical" >
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="#000030"
+        style="@style/somePadding" >
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:orientation="horizontal" >
+            <Button
+                android:id="@+id/map_msg_get"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_msg_get_lbl"
+                android:onClick="onClickGetMessage" />
+            <EditText
+                android:id="@+id/map_msg_handle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:ems="10" />
+            <RadioGroup
+                android:id="@+id/map_msg_get_charset"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:orientation="horizontal" >
+                <RadioButton
+                    android:id="@+id/map_msg_get_charset_native"
+                    android:layout_width="wrap_content"
+                    android:layout_height="match_parent"
+                    android:text="@string/charset_native_lbl" />
+                <RadioButton
+                    android:id="@+id/map_msg_get_charset_utf8"
+                    android:layout_width="wrap_content"
+                    android:layout_height="match_parent"
+                    android:text="@string/charset_utf8_lbl"
+                    android:checked="true" />
+            </RadioGroup>
+            <CheckBox
+                android:id="@+id/map_msg_get_attachment"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_msg_get_attachment_lbl" />
+        </LinearLayout>
+
+        <Button
+            android:id="@+id/map_msg_copy_to_editor"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true"
+            android:text="@string/map_msg_copy_to_editor_lbl"
+            android:onClick="onClickCopyMessage" />
+        <Button
+            android:id="@+id/map_msg_delete"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_delete_message"
+            android:layout_toLeftOf="@id/map_msg_copy_to_editor"
+            android:onClick="onClickDeleteMessage" />
+        <Button
+            android:id="@+id/map_msg_set_status"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_set_unread"
+            android:layout_toLeftOf="@id/map_msg_delete"
+            android:onClick="onClickSetStatus" />
+    </RelativeLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        style="@style/somePadding" >
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1">
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_bmsg_status_prompt" />
+            <EditText
+                android:id="@+id/maptest_bmsg_status"
+                android:editable="false"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1">
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_bmsg_type_prompt" />
+            <EditText
+                android:id="@+id/maptest_bmsg_type"
+                android:editable="false"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="3">
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_bmsg_folder_prompt" />
+            <EditText
+                android:id="@+id/maptest_bmsg_folder"
+                android:editable="false"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        style="@style/somePadding" >
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1">
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_bbody_encoding_prompt" />
+            <EditText
+                android:id="@+id/maptest_bbody_encoding"
+                android:editable="false"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1">
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_bbody_charset_prompt" />
+            <EditText
+                android:id="@+id/maptest_bbody_charset"
+                android:editable="false"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1">
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_bbody_language_prompt" />
+            <EditText
+                android:id="@+id/maptest_bbody_language"
+                android:editable="false"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:background="#303030"
+        style="@style/somePadding" >
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_originators_prompt" />
+        <EditText
+            android:id="@+id/maptest_orig"
+            android:editable="false"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:background="#303030"
+        style="@style/somePadding" >
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_recipients_prompt" />
+        <EditText
+            android:id="@+id/maptest_rcpt"
+            android:editable="false"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+    </LinearLayout>
+
+    <EditText
+        android:id="@+id/maptest_message"
+        android:editable="false"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:typeface="monospace"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        style="@style/somePadding" />
+
+</LinearLayout>
+
+
+<LinearLayout
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:focusableInTouchMode="true"
+    android:orientation="vertical" >
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:background="#000030"
+        style="@style/somePadding" >
+
+        <Button
+            android:id="@+id/map_msg_push"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_msg_push_lbl"
+            android:onClick="onClickPushMessage" />
+        <CheckBox
+            android:id="@+id/map_msg_push_transparent"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_msg_push_transparent_lbl" />
+        <CheckBox
+            android:id="@+id/map_msg_push_retry"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_msg_push_retry_lbl" />
+        <RadioGroup
+            android:id="@+id/map_msg_push_charset"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:orientation="horizontal" >
+            <RadioButton
+                android:id="@+id/map_msg_push_charset_native"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:text="@string/charset_native_lbl" />
+            <RadioButton
+                android:id="@+id/map_msg_push_charset_utf8"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:text="@string/charset_utf8_lbl"
+                android:checked="true" />
+        </RadioGroup>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        style="@style/somePadding" >
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1">
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_bmsg_type_prompt" />
+            <Spinner
+                android:id="@+id/bmsgedit_type"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:entries="@array/map_message_type_array" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1">
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_bbody_encoding_prompt" />
+            <Spinner
+                android:id="@+id/bmsgedit_encoding"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:entries="@array/map_message_encoding_array" />
+        </LinearLayout>
+
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="3" />
+
+    </LinearLayout>
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:background="#303030"
+        style="@style/somePadding" >
+
+        <TextView
+            android:id="@+id/bmsgedit_orig_add_lbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:text="@string/map_originators_prompt" />
+        <Button
+            android:id="@+id/bmsgedit_orig_del"
+            android:text="@string/ppl_del"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true"
+            android:ems="1"
+            android:onClick="onClickRemovePeople" />
+        <Button
+            android:id="@+id/bmsgedit_orig_add"
+            android:text="@string/ppl_add"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toLeftOf="@id/bmsgedit_orig_del"
+            android:ems="1"
+            android:onClick="onClickAddPerson" />
+        <EditText
+            android:id="@+id/bmsgedit_orig"
+            android:editable="false"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_toLeftOf="@id/bmsgedit_orig_add"
+            android:layout_toRightOf="@id/bmsgedit_orig_add_lbl" />
+
+    </RelativeLayout>
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:background="#303030"
+        style="@style/somePadding" >
+
+        <TextView
+            android:id="@+id/bmsgedit_rcpt_add_lbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:text="@string/map_recipients_prompt" />
+        <Button
+            android:id="@+id/bmsgedit_rcpt_del"
+            android:text="@string/ppl_del"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true"
+            android:ems="1"
+            android:onClick="onClickRemovePeople" />
+        <Button
+            android:id="@+id/bmsgedit_rcpt_add"
+            android:text="@string/ppl_add"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toLeftOf="@id/bmsgedit_rcpt_del"
+            android:ems="1"
+            android:onClick="onClickAddPerson" />
+        <EditText
+            android:id="@+id/bmsgedit_rcpt"
+            android:editable="false"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_toLeftOf="@id/bmsgedit_rcpt_add"
+            android:layout_toRightOf="@id/bmsgedit_rcpt_add_lbl" />
+
+    </RelativeLayout>
+
+    <EditText
+        android:id="@+id/bmsgedit_contents"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:typeface="monospace"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        style="@style/somePadding" />
+
+</LinearLayout>
+
+</ViewFlipper>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/activity_message_filter.xml b/bttestapp/res/layout/activity_message_filter.xml
new file mode 100644
index 0000000..6f0334c
--- /dev/null
+++ b/bttestapp/res/layout/activity_message_filter.xml
@@ -0,0 +1,147 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="horizontal" >
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:orientation="vertical" >
+
+        <CheckBox
+            android:id="@+id/param_subject"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_parameter_subject" />
+
+        <CheckBox
+            android:id="@+id/param_datatime"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_parameter_datetime" />
+
+        <CheckBox
+            android:id="@+id/param_sender_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_parameter_sender_name" />
+
+        <CheckBox
+            android:id="@+id/param_sender_addressing"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_parameter_sender_addressing" />
+
+        <CheckBox
+            android:id="@+id/param_receipent_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_parameter_recipient_name" />
+
+        <CheckBox
+            android:id="@+id/param_receipent_addressing"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_parameter_recipient_addressing" />
+
+        <CheckBox
+            android:id="@+id/param_type"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_parameter_type" />
+
+        <CheckBox
+            android:id="@+id/param_size"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_parameter_size" />
+
+        <CheckBox
+            android:id="@+id/param_reception_status"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_parameter_reception_status" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:orientation="vertical" >
+
+         <CheckBox
+            android:id="@+id/param_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_parameter_text" />
+
+        <CheckBox
+            android:id="@+id/param_attachment_size"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_parameter_attachment_size" />
+
+        <CheckBox
+            android:id="@+id/param_priority"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_parameter_priority" />
+
+        <CheckBox
+            android:id="@+id/param_read"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_parameter_read" />
+
+        <CheckBox
+            android:id="@+id/param_sent"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_parameter_sent" />
+
+        <CheckBox
+            android:id="@+id/param_protected"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_parameter_protected" />
+
+        <CheckBox
+            android:id="@+id/param_replayto_addressing"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/map_parameter_replyto_addressing" />
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/activity_pbap_test.xml b/bttestapp/res/layout/activity_pbap_test.xml
new file mode 100644
index 0000000..87561ec
--- /dev/null
+++ b/bttestapp/res/layout/activity_pbap_test.xml
@@ -0,0 +1,561 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<ViewFlipper xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/pbap_test_viewflipper"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" >
+
+<RelativeLayout
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:orientation="horizontal" >
+
+        <RelativeLayout
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            android:orientation="vertical" >
+
+            <TextView
+                android:id="@+id/label_download_phonebook_prompt"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentTop="true"
+                android:text="@string/phonebook_prompt" />
+
+            <Spinner
+                android:id="@+id/pbap_download_spinner"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:entries="@array/phonebook_arrays"
+                android:prompt="@string/phonebook_prompt"
+                android:layout_alignParentLeft="true"
+                android:layout_below="@+id/label_download_phonebook_prompt"/>
+
+            <TextView
+                android:id="@+id/label_download_format_prompt"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/pbap_download_spinner"
+                android:layout_marginTop="5dp"
+                android:text="@string/vcard_format_prompt" />
+
+            <RadioGroup
+                android:id="@+id/pbap_download_formats"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/label_download_format_prompt"
+                android:orientation="horizontal" >
+
+                <RadioButton
+                    android:id="@+id/pbap_download_vcard21"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/vcard21"
+                    android:checked="true" />
+
+                <RadioButton
+                    android:id="@+id/pbap_download_vcard30"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/vcard30" />
+            </RadioGroup>
+
+            <LinearLayout
+                android:id="@+id/pbap_layout_1"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/pbap_download_formats"
+                android:layout_marginTop="5dp" >
+                <TextView
+                    android:id="@+id/label_download_list_count_prompt"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/max_list_count_prompt"
+                    android:layout_weight="1.0" />
+                <TextView
+                    android:id="@+id/label_download_offset_prompt"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/offset_prompt"
+                    android:layout_weight="1.0" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:id="@+id/pbap_layout_2"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/pbap_layout_1"
+                android:layout_marginTop="5dp" >
+                <EditText
+                    android:id="@+id/pbap_download_max_list_count"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ems="10"
+                    android:inputType="number"
+                    android:layout_weight="1.0" />
+                <EditText
+                    android:id="@+id/pbap_download_offset_value"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ems="10"
+                    android:inputType="number"
+                    android:layout_weight="1.0" />
+            </LinearLayout>
+
+            <Button
+                android:id="@+id/pbap_download_filter_button"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_button"
+                android:layout_below="@+id/pbap_layout_2"
+                android:layout_marginTop="5dp" />
+
+            <Button
+                android:id="@+id/pbap_download_search"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:layout_alignParentRight="true"
+                android:text="@string/search"
+                android:layout_marginBottom="15dp"
+                android:layout_marginRight="15dp" />
+
+            <Button
+                android:id="@+id/pbap_download_abort"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:layout_toLeftOf="@id/pbap_download_search"
+                android:text="@string/abort"
+                android:layout_marginBottom="15dp"
+                android:layout_marginRight="15dp"
+                android:onClick="onClick_abort" />
+
+            <Button
+                android:id="@+id/pbap_download_getsize"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:layout_toLeftOf="@id/pbap_download_abort"
+                android:text="@string/getsize"
+                android:layout_marginBottom="15dp"
+                android:layout_marginRight="15dp"
+                android:onClick="onClick_download_getsize" />
+
+        </RelativeLayout>
+
+        <LinearLayout
+            android:id="@+id/pbap_download_layout_listview"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:layout_weight="1"
+            android:orientation="vertical" >
+            <ListView android:id="@+id/pbap_download_listview_contacts_list"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+            <ProgressBar
+                android:id="@+id/pbap_download_progress_bar"
+                style="?android:attr/progressBarStyleLarge"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical|center_horizontal" />
+
+            <TextView
+                android:id="@+id/pbap_download_test_list_empty"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:text="@string/list_empty" />
+        </LinearLayout>
+    </LinearLayout>
+</RelativeLayout>
+
+<RelativeLayout
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:orientation="horizontal" >
+
+        <RelativeLayout
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            android:orientation="vertical" >
+
+            <TextView
+                android:id="@+id/label_browse_phonebook_prompt"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentTop="true"
+                android:text="@string/phonebook_prompt" />
+
+             <LinearLayout
+                android:id="@+id/pbap_browse_pb_group"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                android:layout_below="@+id/label_browse_phonebook_prompt" >
+
+                 <Spinner
+                     android:id="@+id/pbap_browse_spinner"
+                     android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"
+                     android:layout_weight="1"
+                     android:entries="@array/phonebook_arrays"
+                     android:prompt="@string/phonebook_prompt" />
+
+                 <Button
+                     android:id="@+id/pbap_browse_set_phonebook"
+                     android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"
+                     android:layout_weight="1"
+                     android:text="@string/set_phonebook"
+                     android:layout_marginRight="15dp"
+                     android:tag="pbap_browse_spinner"
+                     android:onClick="onClick_setPhonebook" />
+             </LinearLayout>
+
+
+            <TextView
+                android:id="@+id/label_search_attribute_prompt"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/pbap_browse_pb_group"
+                android:layout_marginTop="5dp"
+                android:text="@string/search_attribute_prompt" />
+
+            <RadioGroup
+                android:id="@+id/search_attributes"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/label_search_attribute_prompt"
+                android:orientation="horizontal" >
+
+                <RadioButton
+                    android:id="@+id/pbap_browse_name"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/search_attribute_name"
+                    android:checked="true" />
+
+                <RadioButton
+                    android:id="@+id/pbap_browse_number"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/search_attribute_number" />
+
+                <RadioButton
+                    android:id="@+id/pbap_browse_sound"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/search_attribute_sound" />
+            </RadioGroup>
+
+            <TextView
+                android:id="@+id/label_search_value"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/search_attributes"
+                android:layout_marginTop="5dp"
+                android:text="@string/search_value_prompt" />
+
+            <EditText
+                android:id="@+id/pbap_browse_search_value"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/label_search_value"
+                android:ems="10" />
+
+            <LinearLayout
+                android:id="@+id/pbap_layout_3"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/pbap_browse_search_value"
+                android:layout_marginTop="5dp" >
+                <TextView
+                    android:id="@+id/label_browse_list_count_prompt"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/max_list_count_prompt"
+                    android:layout_weight="1.0" />
+                <TextView
+                    android:id="@+id/label_browse_offset_prompt"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/offset_prompt"
+                    android:layout_weight="1.0" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:id="@+id/pbap_layout_4"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/pbap_layout_3"
+                android:layout_marginTop="5dp" >
+                <EditText
+                    android:id="@+id/pbap_browse_max_list_count"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ems="10"
+                    android:inputType="number"
+                    android:layout_weight="1.0" />
+                <EditText
+                    android:id="@+id/pbap_browse_offset_value"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ems="10"
+                    android:inputType="number"
+                    android:layout_weight="1.0" />
+            </LinearLayout>
+
+            <RadioGroup
+                android:id="@+id/order_attribute"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/pbap_layout_4"
+                android:orientation="horizontal" >
+
+                <RadioButton
+                    android:id="@+id/pbap_browse_unordered"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/order_attribute_unordered"
+                    android:checked="true" />
+
+                <RadioButton
+                    android:id="@+id/pbap_browse_alphabetical"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/order_attribute_alphabetical" />
+
+                <RadioButton
+                    android:id="@+id/pbap_browse_indexed"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/order_attribute_indexed" />
+
+                <RadioButton
+                    android:id="@+id/pbap_browse_phonetic"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/order_attribute_phonetic" />
+            </RadioGroup>
+
+            <Button
+                android:id="@+id/pbap_browse_search"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:layout_alignParentRight="true"
+                android:text="@string/search"
+                android:layout_marginBottom="15dp"
+                android:layout_marginRight="15dp" />
+
+
+            <Button
+                android:id="@+id/pbap_browse_abort"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:layout_toLeftOf="@id/pbap_browse_search"
+                android:text="@string/abort"
+                android:layout_marginBottom="15dp"
+                android:layout_marginRight="15dp"
+                android:onClick="onClick_abort" />
+
+            <Button
+                android:id="@+id/pbap_browse_getsize"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:layout_toLeftOf="@id/pbap_browse_abort"
+                android:text="@string/getsize"
+                android:layout_marginBottom="15dp"
+                android:layout_marginRight="15dp"
+                android:onClick="onClick_browse_getsize" />
+
+        </RelativeLayout>
+
+        <LinearLayout
+            android:id="@+id/pbap_browse_layout_listview"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:layout_weight="1"
+            android:orientation="vertical" >
+            <ListView android:id="@+id/pbap_browse_listview_contacts_list"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+            <ProgressBar
+                android:id="@+id/pbap_browse_progress_bar"
+                style="?android:attr/progressBarStyleLarge"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical|center_horizontal" />
+
+            <TextView
+                android:id="@+id/pbap_browse_test_list_empty"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:text="@string/list_empty" />
+        </LinearLayout>
+    </LinearLayout>
+</RelativeLayout>
+
+<RelativeLayout
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:orientation="horizontal" >
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_weight="1" >
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/phonebook_prompt" />
+
+             <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal" >
+
+                 <Spinner
+                     android:id="@+id/pbap_vcard_spinner"
+                     android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"
+                     android:layout_weight="1"
+                     android:entries="@array/phonebook_arrays"
+                     android:prompt="@string/phonebook_prompt" />
+
+                 <Button
+                     android:id="@+id/pbap_vcard_set_phonebook"
+                     android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"
+                     android:layout_weight="1"
+                     android:text="@string/set_phonebook"
+                     android:layout_marginRight="15dp"
+                     android:tag="pbap_vcard_spinner"
+                     android:onClick="onClick_setPhonebook" />
+             </LinearLayout>
+
+             <TextView
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+                 android:text="@string/vcard_header_prompt" />
+
+             <EditText
+                 android:id="@+id/pbap_vcard_header"
+                 android:layout_width="match_parent"
+                 android:layout_height="wrap_content"
+                 android:ems="10"
+                 android:inputType="text" />
+
+             <TextView
+                 android:layout_width="match_parent"
+                 android:layout_height="wrap_content"
+                 android:layout_marginTop="5dp"
+                 android:text="@string/vcard_format_prompt" />
+
+             <RadioGroup
+                 android:id="@+id/pbab_vcard_formats"
+                 android:layout_width="match_parent"
+                 android:layout_height="wrap_content"
+                 android:orientation="horizontal" >
+
+                 <RadioButton
+                     android:id="@+id/pbap_vcard_vcard21"
+                     android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"
+                     android:text="@string/vcard21"
+                     android:checked="true"
+                     android:onClick="onClickRadioButtonVcardType21" />
+
+                 <RadioButton
+                     android:id="@+id/pbap_vcard_vcard30"
+                     android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"
+                     android:text="@string/vcard30"
+                     android:onClick="onClickRadioButtonVcardType30" />
+             </RadioGroup>
+
+             <Button
+                 android:id="@+id/pbap_vcard_filter_button"
+                 android:layout_width="match_parent"
+                 android:layout_height="wrap_content"
+                 android:text="@string/filter_button"
+                 android:layout_marginTop="5dp"
+                 android:onClick="onClickVcardFilterAttributes"/>
+
+             <Button
+                 android:id="@+id/pbap_vcard_get_vcard"
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+                 android:layout_gravity="right"
+                 android:layout_marginTop="50dp"
+                 android:text="@string/get_vcard"
+                 android:onClick="onClickVcardButtonGet" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:layout_weight="1" >
+
+            <org.codeaurora.bluetooth.bttestapp.VcardView
+                android:id="@+id/pbap_vcard_vcardview"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent" />
+
+        </LinearLayout>
+    </LinearLayout>
+</RelativeLayout>
+</ViewFlipper>
diff --git a/bttestapp/res/layout/activity_vcard_filter.xml b/bttestapp/res/layout/activity_vcard_filter.xml
new file mode 100644
index 0000000..8b41dcd
--- /dev/null
+++ b/bttestapp/res/layout/activity_vcard_filter.xml
@@ -0,0 +1,202 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" >
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:baselineAligned="false" >
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:orientation="vertical" >
+            <CheckBox
+                android:id="@+id/filter_chooser_version"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_version" />
+            <CheckBox
+                android:id="@+id/filter_chooser_fn"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_fn" />
+            <CheckBox
+                android:id="@+id/filter_chooser_n"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_n" />
+            <CheckBox
+                android:id="@+id/filter_chooser_photo"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_photo" />
+            <CheckBox
+                android:id="@+id/filter_chooser_bday"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_bday" />
+            <CheckBox
+                android:id="@+id/filter_chooser_adr"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_adr" />
+            <CheckBox
+                android:id="@+id/filter_chooser_label"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_label" />
+            <CheckBox
+                android:id="@+id/filter_chooser_tel"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_tel" />
+            <CheckBox
+                android:id="@+id/filter_chooser_email"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_email" />
+            <CheckBox
+                android:id="@+id/filter_chooser_mailer"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_mailer" />
+            <CheckBox
+                android:id="@+id/filter_chooser_tz"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_tz" />
+            <CheckBox
+                android:id="@+id/filter_chooser_geo"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_geo" />
+            <CheckBox
+                android:id="@+id/filter_chooser_title"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_title" />
+            <CheckBox
+                android:id="@+id/filter_chooser_role"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_role" />
+            <CheckBox
+                android:id="@+id/filter_chooser_logo"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_logo" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:orientation="vertical">
+            <CheckBox
+                android:id="@+id/filter_chooser_agent"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_agent" />
+            <CheckBox
+                android:id="@+id/filter_chooser_org"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_org" />
+            <CheckBox
+                android:id="@+id/filter_chooser_note"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_note" />
+            <CheckBox
+                android:id="@+id/filter_chooser_rev"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_rev" />
+            <CheckBox
+                android:id="@+id/filter_chooser_sound"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_sound" />
+            <CheckBox
+                android:id="@+id/filter_chooser_url"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_url" />
+            <CheckBox
+                android:id="@+id/filter_chooser_uid"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_uid" />
+            <CheckBox
+                android:id="@+id/filter_chooser_key"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_key" />
+            <CheckBox
+                android:id="@+id/filter_chooser_nickname"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_nickname" />
+            <CheckBox
+                android:id="@+id/filter_chooser_categories"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_categories" />
+            <CheckBox
+                android:id="@+id/filter_chooser_proid"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_proid" />
+            <CheckBox
+                android:id="@+id/filter_chooser_class"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_class" />
+            <CheckBox
+                android:id="@+id/filter_chooser_sort_string"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_sort_string" />
+            <CheckBox
+                android:id="@+id/filter_chooser_x_irmc_call_datetime"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_x_irmc_call_datetime" />
+            <CheckBox
+                android:id="@+id/filter_chooser_filter"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/filter_field_filter" />
+        </LinearLayout>
+    </LinearLayout>
+</ScrollView>
diff --git a/bttestapp/res/layout/call_view.xml b/bttestapp/res/layout/call_view.xml
new file mode 100644
index 0000000..a415f41
--- /dev/null
+++ b/bttestapp/res/layout/call_view.xml
@@ -0,0 +1,182 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:orientation="horizontal" >
+
+    <TextView
+        android:id="@+id/call_id"
+        android:layout_width="50dip"
+        android:layout_height="50dip"
+        android:layout_marginTop="5dp"
+        android:layout_marginBottom="5dp"
+        android:layout_marginLeft="5dp"
+        android:layout_marginRight="10dp"
+        android:gravity="center|center_vertical"
+        android:background="@color/call_unknown"
+        android:textAppearance="?android:attr/textAppearanceLarge" />
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight=".4"
+        android:orientation="vertical" >
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center_vertical"
+            android:orientation="horizontal"
+            tools:ignore="UseCompoundDrawables" >
+
+            <ImageView
+                android:id="@+id/call_direction"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="10dp"
+                android:layout_marginRight="10dp"
+                tools:ignore="ContentDescription"/>
+
+            <TextView
+                android:id="@+id/call_number"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textAppearance="?android:attr/textAppearanceLarge" />
+
+        </LinearLayout>
+
+        <TextView
+            android:id="@+id/call_multiparty"
+            style="@style/indicator"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="5dip"
+            android:paddingRight="5dip"
+            android:background="@color/ind_off"
+            android:text="@string/call_multiparty" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight=".6"
+        android:orientation="vertical" >
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal" >
+
+            <TextView
+                style="@style/indicator"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:visibility="invisible" />
+
+            <TextView
+                android:id="@+id/call_state_active"
+                style="@style/indicator"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:background="@color/call_active"
+                android:text="@string/call_state_active" />
+
+            <TextView
+                android:id="@+id/call_state_held"
+                style="@style/indicator"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:background="@color/call_held"
+                android:text="@string/call_state_held" />
+
+            <TextView
+                android:id="@+id/call_state_held_by_rnh"
+                style="@style/indicator"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:background="@color/call_held"
+                android:text="@string/call_state_held_by_rnh" />
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal" >
+
+            <TextView
+                android:id="@+id/call_state_dialing"
+                style="@style/indicator"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:background="@color/call_new"
+                android:text="@string/call_state_dialing" />
+
+            <TextView
+                android:id="@+id/call_state_alerting"
+                style="@style/indicator"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:background="@color/call_new"
+                android:text="@string/call_state_alerting" />
+
+            <TextView
+                android:id="@+id/call_state_incoming"
+                style="@style/indicator"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:background="@color/call_new"
+                android:text="@string/call_state_incoming" />
+
+            <TextView
+                android:id="@+id/call_state_waiting"
+                style="@style/indicator"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:background="@color/call_new"
+                android:text="@string/call_state_waiting" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/calls_list_fragment.xml b/bttestapp/res/layout/calls_list_fragment.xml
new file mode 100644
index 0000000..a3f3eb2
--- /dev/null
+++ b/bttestapp/res/layout/calls_list_fragment.xml
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <ListView
+        android:id="@+id/calls_list"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <Button
+                android:id="@+id/call_action_accept"
+                android:layout_height="wrap_content"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:text="@string/call_action_accept" />
+
+            <Button
+                android:id="@+id/call_action_hold_and_accept"
+                android:layout_height="wrap_content"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:text="@string/call_action_hold_and_accept" />
+
+            <Button
+                android:id="@+id/call_action_release_and_accept"
+                android:layout_height="wrap_content"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:text="@string/call_action_release_and_accept" />
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <Button
+                android:id="@+id/call_action_reject"
+                android:layout_height="wrap_content"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:text="@string/call_action_reject" />
+
+            <Button
+                android:id="@+id/call_action_terminate"
+                android:layout_height="wrap_content"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:text="@string/call_action_terminate" />
+
+            <Button
+                android:id="@+id/call_action_hold"
+                android:layout_height="wrap_content"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:text="@string/call_action_hold" />
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <Button
+                android:id="@+id/call_action_respond_and_hold"
+                android:layout_height="wrap_content"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:text="@string/call_action_respond_and_hold" />
+
+            <Button
+                android:id="@+id/call_action_private_mode"
+                android:layout_height="wrap_content"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:text="@string/call_action_private_mode" />
+
+            <Button
+                android:id="@+id/call_action_explicit_transfer"
+                android:layout_height="wrap_content"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:text="@string/call_action_explicit_transfer" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/date_time_picker.xml b/bttestapp/res/layout/date_time_picker.xml
new file mode 100644
index 0000000..ea567aa
--- /dev/null
+++ b/bttestapp/res/layout/date_time_picker.xml
@@ -0,0 +1,46 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="horizontal" >
+
+    <DatePicker
+        android:id="@+id/date_picker"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:calendarViewShown="false" />
+
+    <TimePicker
+        android:id="@+id/time_picker"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/dialpad_fragment.xml b/bttestapp/res/layout/dialpad_fragment.xml
new file mode 100644
index 0000000..ae33855
--- /dev/null
+++ b/bttestapp/res/layout/dialpad_fragment.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" >
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <EditText
+            android:id="@+id/dialpad_number"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:editable="false"
+            android:singleLine="true"
+            android:ellipsize="start"
+            android:scrollHorizontally="true"
+            android:hint="@string/dialpad_number_hint"
+            tools:ignore="LabelFor,TextFields,Deprecated" />
+
+        <ImageButton
+            android:id="@+id/dialpad_del"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:contentDescription="@string/dialpad_del"
+            android:src="@drawable/sym_keyboard_delete" />
+
+    </LinearLayout>
+
+    <GridView
+        android:id="@+id/dialpad"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:numColumns="4" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical" >
+
+        <Button
+            android:id="@+id/dialpad_dial"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1.5"
+            android:text="@string/dialpad_dial" />
+
+        <Button
+            android:id="@+id/dialpad_memdial"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1.5"
+            android:text="@string/dialpad_memdial" />
+
+        <ToggleButton
+            android:id="@+id/dialpad_dtmf"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:textOn="@string/dialpad_dtmf"
+            android:textOff="@string/dialpad_dtmf" />
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/indicators_fragment.xml b/bttestapp/res/layout/indicators_fragment.xml
new file mode 100644
index 0000000..473bc81
--- /dev/null
+++ b/bttestapp/res/layout/indicators_fragment.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" >
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <ToggleButton
+            android:id="@+id/ind_conn_state"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:textOn="@string/ind_conn_state"
+            android:textOff="@string/ind_conn_state" />
+
+        <ToggleButton
+            android:id="@+id/ind_audio_state"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:textOn="@string/ind_audio_state"
+            android:textOff="@string/ind_audio_state" />
+
+        <ToggleButton
+            android:id="@+id/ind_vr_state"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:textOn="@string/ind_vr_state"
+            android:textOff="@string/ind_vr_state" />
+
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/ind_operator"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:text="@string/ind_operator_unknown"
+        android:textAppearance="?android:attr/textAppearanceLarge" />
+
+    <TextView
+        android:id="@+id/ind_subscriber"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:text="@string/ind_subscriber_unknown" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <TextView
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:gravity="right"
+            android:text="@string/ind_signal_level"
+            style="@style/indicator"
+            android:background="#00000000" />
+
+        <TextView
+            android:id="@+id/ind_signal_level"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:gravity="left"
+            style="@style/indicator"
+            android:background="#00000000" />
+
+        <TextView
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:gravity="right"
+            android:text="@string/ind_battery_level"
+            style="@style/indicator"
+            android:background="#00000000" />
+
+        <TextView
+            android:id="@+id/ind_battery_level"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:gravity="left"
+            style="@style/indicator"
+            android:background="#00000000" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <TextView
+            android:id="@+id/ind_network_state"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/ind_network_state"
+            style="@style/indicator" />
+
+        <TextView
+            android:id="@+id/ind_roaming_state"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/ind_roaming_state"
+            style="@style/indicator" />
+
+        <TextView
+            android:id="@+id/ind_inband_state"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/ind_inband_state"
+            style="@style/indicator" />
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/message_row.xml b/bttestapp/res/layout/message_row.xml
new file mode 100644
index 0000000..11a051b
--- /dev/null
+++ b/bttestapp/res/layout/message_row.xml
@@ -0,0 +1,179 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingTop="2dp"
+    android:paddingBottom="2dp" >
+
+    <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="#303030"
+            style="@style/someSmallPadding" >
+
+        <TextView
+            android:id="@+id/message_row_date"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingRight="20dp"
+            android:typeface="monospace"
+            android:layout_alignParentLeft="true" />
+
+        <TextView
+            android:id="@+id/message_row_type"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ems="6"
+            android:typeface="monospace"
+            android:paddingRight="20dp"
+            android:layout_toRightOf="@id/message_row_date" />
+
+        <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_toRightOf="@id/message_row_type"
+                android:orientation="horizontal" >
+
+            <TextView
+                android:id="@+id/message_row_flag_text"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginRight="3dp"
+                android:typeface="monospace"
+                android:text="@string/msgflag_text" />
+
+            <TextView
+                android:id="@+id/message_row_flag_read"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="3dp"
+                android:layout_marginRight="3dp"
+                android:typeface="monospace"
+                android:text="@string/msgflag_read" />
+
+            <TextView
+                android:id="@+id/message_row_flag_sent"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="3dp"
+                android:layout_marginRight="3dp"
+                android:typeface="monospace"
+                android:text="@string/msgflag_sent" />
+
+            <TextView
+                android:id="@+id/message_row_flag_drm"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="3dp"
+                android:layout_marginRight="3dp"
+                android:typeface="monospace"
+                android:text="@string/msgflag_drm" />
+
+            <TextView
+                android:id="@+id/message_row_flag_prio"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="3dp"
+                android:typeface="monospace"
+                android:text="@string/msgflag_prio" />
+
+        </LinearLayout>
+
+        <TextView
+            android:id="@+id/message_row_handle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:typeface="monospace"
+            android:layout_alignParentRight="true" />
+
+    </RelativeLayout>
+
+    <TextView
+        android:id="@+id/message_row_subject"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textStyle="bold"
+        android:background="#202020"
+        style="@style/someSmallPadding" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:background="#202020"
+        style="@style/someSmallPadding" >
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:orientation="horizontal" >
+
+            <TextView
+                android:id="@+id/message_row_from_lbl"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textStyle="bold"
+                android:layout_marginRight="5dp"
+                android:text="@string/map_msg_row_from" />
+
+            <TextView
+                android:id="@+id/message_row_from"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:orientation="horizontal" >
+
+            <TextView
+                android:id="@+id/message_row_to_lbl"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textStyle="bold"
+                android:layout_marginRight="5dp"
+                android:text="@string/map_msg_row_to" />
+
+            <TextView
+                android:id="@+id/message_row_to"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/messages_filter.xml b/bttestapp/res/layout/messages_filter.xml
new file mode 100644
index 0000000..ab205da
--- /dev/null
+++ b/bttestapp/res/layout/messages_filter.xml
@@ -0,0 +1,235 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:orientation="vertical" >
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_filter_without_message_type" />
+
+            <CheckBox
+                android:id="@+id/map_filter_type_sms_gsm"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_msg_type_sms_gsm" />
+
+            <CheckBox
+                android:id="@+id/map_filter_type_sms_cdma"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_msg_type_sms_cdma" />
+
+            <CheckBox
+                android:id="@+id/map_filter_type_email"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_msg_type_email" />
+
+            <CheckBox
+                android:id="@+id/map_filter_type_mms"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_msg_type_mms" />
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:orientation="vertical">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_filter_read_status" />
+
+            <RadioGroup
+                android:id="@+id/map_filter_read_status"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:orientation="vertical" >
+
+                <RadioButton
+                    android:id="@+id/map_filter_read_status_all"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/map_filter_any_status"
+                    android:checked="true" />
+
+                <RadioButton
+                    android:id="@+id/map_filter_read_status_unread"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/map_filter_read_status_unread" />
+
+                <RadioButton
+                    android:id="@+id/map_filter_read_status_read"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/map_filter_read_status_read" />
+
+            </RadioGroup>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:orientation="vertical" >
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_filter_priority" />
+
+            <RadioGroup
+                android:id="@+id/map_filter_priority"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:orientation="vertical" >
+
+                <RadioButton
+                    android:id="@+id/map_filter_priority_all"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/map_filter_any_priority"
+                    android:checked="true" />
+
+                <RadioButton
+                    android:id="@+id/map_filter_priority_high"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/map_filter_priority_high" />
+
+                <RadioButton
+                    android:id="@+id/map_filter_status_non_high"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/map_filter_priority_non_high" />
+
+            </RadioGroup>
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <TableLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:focusableInTouchMode="true"
+        android:stretchColumns="1" >
+
+        <TableRow>
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_filter_period_begin" />
+
+            <EditText
+                android:id="@+id/map_filter_period_begin"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+            <ImageButton
+                android:id="@+id/map_pick_data_period_begin"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:src="@android:drawable/ic_menu_today" />
+
+        </TableRow>
+
+        <TableRow>
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_filter_period_end" />
+
+            <EditText
+                android:id="@+id/map_filter_period_end"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+            <ImageButton
+                android:id="@+id/map_pick_data_period_end"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:src="@android:drawable/ic_menu_today" />
+        </TableRow>
+
+    </TableLayout>
+
+    <TableLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:stretchColumns="1" >
+
+        <TableRow>
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_filter_recipient" />
+
+            <EditText
+                android:id="@+id/map_filter_recipient"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+        </TableRow>
+
+        <TableRow>
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_filter_originator" />
+
+            <EditText
+                android:id="@+id/map_filter_originator"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+        </TableRow>
+    </TableLayout>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/pbap_auth.xml b/bttestapp/res/layout/pbap_auth.xml
new file mode 100644
index 0000000..b3f7988
--- /dev/null
+++ b/bttestapp/res/layout/pbap_auth.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright (c) 2013, The Linux Foundation. All rights reserved.
+** Not a Contribution.
+** Copyright 2009, 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.
+*/
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent">
+
+    <LinearLayout
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/message"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="20dip"
+        android:layout_marginRight="20dip"
+        android:gravity="center_horizontal"
+        android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    <EditText
+        android:id="@+id/text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="20dip"
+        android:layout_marginLeft="20dip"
+        android:layout_marginRight="20dip"
+        android:singleLine="true" />
+
+    </LinearLayout>
+
+</ScrollView>
diff --git a/bttestapp/res/layout/service_row.xml b/bttestapp/res/layout/service_row.xml
new file mode 100644
index 0000000..975b0e6
--- /dev/null
+++ b/bttestapp/res/layout/service_row.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:padding="4dp" >
+
+    <LinearLayout
+        android:id="@+id/service_label"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_centerVertical="true"
+        android:layout_alignParentLeft="true" >
+
+        <TextView
+            android:id="@+id/service_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceLarge" />
+
+        <TextView
+            android:id="@+id/service_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceSmall" />
+
+    </LinearLayout>
+
+    <Switch
+        android:id="@+id/service_switch"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_alignParentRight="true"
+        android:focusable="false" />
+
+    <Switch
+        android:id="@+id/notification_switch"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_toLeftOf="@id/service_switch"
+        android:focusable="false"
+        android:visibility="gone" />
+
+</RelativeLayout>
diff --git a/bttestapp/res/layout/string_picker_dialog.xml b/bttestapp/res/layout/string_picker_dialog.xml
new file mode 100644
index 0000000..2ca09d5
--- /dev/null
+++ b/bttestapp/res/layout/string_picker_dialog.xml
@@ -0,0 +1,41 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+
+    <EditText
+        android:id="@+id/text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:inputType="text"/>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/vcard_row.xml b/bttestapp/res/layout/vcard_row.xml
new file mode 100644
index 0000000..5458a9b
--- /dev/null
+++ b/bttestapp/res/layout/vcard_row.xml
@@ -0,0 +1,56 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:padding="4dip" >
+
+    <ImageView
+        android:id="@+id/vcard_photo"
+        android:contentDescription="@string/blank"
+        android:layout_width="50dip"
+        android:layout_height="50dip"
+        android:layout_gravity="center_vertical"
+        android:scaleType="centerCrop"
+        android:src="@drawable/ic_contact_picture" />
+
+    <TextView
+        android:id="@+id/vcard_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginLeft="15dp"
+        android:ellipsize="end"
+        android:maxLines="1"
+        android:text="@string/blank"
+        android:textAppearance="?android:attr/textAppearanceLarge" />
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/vcard_view.xml b/bttestapp/res/layout/vcard_view.xml
new file mode 100644
index 0000000..4a9a4e6
--- /dev/null
+++ b/bttestapp/res/layout/vcard_view.xml
@@ -0,0 +1,90 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/scrollView1"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" >
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:padding="15dp" >
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingBottom="15dp"
+            android:orientation="horizontal" >
+
+            <ImageView
+                android:id="@+id/contact_photo"
+                android:contentDescription="@string/blank"
+                android:layout_width="50dip"
+                android:layout_height="50dip"
+                android:layout_gravity="center_vertical"
+                android:scaleType="centerCrop"
+                android:src="@drawable/ic_contact_picture" />
+
+            <TextView
+                android:id="@+id/contact_display_name"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:paddingLeft="15dp"
+                android:textAppearance="?android:attr/textAppearanceLarge" />
+
+        </LinearLayout>
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/contact_phonelist_hdr"
+            style="?android:attr/listSeparatorTextViewStyle" />
+        <LinearLayout
+            android:id="@+id/contact_phonelist"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/contact_emaillist_hdr"
+            style="?android:attr/listSeparatorTextViewStyle" />
+        <LinearLayout
+            android:id="@+id/contact_emaillist"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical" />
+
+    </LinearLayout>
+
+</ScrollView>
diff --git a/bttestapp/res/menu/menu_filter.xml b/bttestapp/res/menu/menu_filter.xml
new file mode 100644
index 0000000..1ffb500
--- /dev/null
+++ b/bttestapp/res/menu/menu_filter.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:id="@+id/menu_save"
+          android:icon="@drawable/ic_menu_save"
+          android:title="@string/menu_save"
+          android:showAsAction="withText|ifRoom" />
+
+</menu>
diff --git a/bttestapp/res/menu/menu_hfp_test.xml b/bttestapp/res/menu/menu_hfp_test.xml
new file mode 100644
index 0000000..bea483f
--- /dev/null
+++ b/bttestapp/res/menu/menu_hfp_test.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:id="@+id/menu_call_history"
+          android:title="@string/menu_call_history"
+          android:showAsAction="ifRoom|withText" />
+
+    <item android:id="@+id/menu_request_phone_num"
+          android:title="@string/menu_request_phone_num"
+          android:showAsAction="ifRoom|withText" />
+
+</menu>
diff --git a/bttestapp/res/menu/menu_map_test.xml b/bttestapp/res/menu/menu_map_test.xml
new file mode 100644
index 0000000..e9ac920
--- /dev/null
+++ b/bttestapp/res/menu/menu_map_test.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- Three-dots submenu items -->
+    <item android:id="@+id/menu_map_update_inbox"
+          android:title="@string/map_update_inbox"
+          android:showAsAction="never"
+          android:visible="false" />
+
+    <item android:id="@+id/menu_map_connect"
+          android:title="@string/menu_connect"
+          android:showAsAction="never"
+          android:visible="false" />
+
+    <item android:id="@+id/menu_map_disconnect"
+          android:title="@string/menu_disconnect"
+          android:showAsAction="never"
+          android:visible="false" />
+
+    <item
+        android:id="@+id/menu_map_goto_inbox"
+        android:title="@string/map_goto_inbox"
+        android:showAsAction="never" />
+
+    <item
+        android:id="@+id/menu_map_goto_outbox"
+        android:title="@string/map_goto_outbox"
+        android:showAsAction="never" />
+
+    <item
+        android:id="@+id/menu_map_goto_draft"
+        android:title="@string/map_goto_draft"
+        android:showAsAction="never" />
+</menu>
diff --git a/bttestapp/res/menu/menu_pbap_test.xml b/bttestapp/res/menu/menu_pbap_test.xml
new file mode 100644
index 0000000..c117c17
--- /dev/null
+++ b/bttestapp/res/menu/menu_pbap_test.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/menu_pbap_connect"
+          android:icon="@drawable/ic_bt_disconnected"
+          android:title="@string/menu_connect"
+          android:showAsAction="withText|always"
+          android:visible="false" />
+
+    <item android:id="@+id/menu_pbap_disconnect"
+          android:icon="@drawable/ic_bt_connected"
+          android:title="@string/menu_disconnect"
+          android:showAsAction="withText|always"
+          android:visible="false" />
+</menu>
diff --git a/bttestapp/res/values-v11/styles.xml b/bttestapp/res/values-v11/styles.xml
new file mode 100644
index 0000000..e8dcb93
--- /dev/null
+++ b/bttestapp/res/values-v11/styles.xml
@@ -0,0 +1,40 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<resources>
+
+    <!--
+        Base application theme for API 11+. This theme completely replaces
+        AppBaseTheme from res/values/styles.xml on API 11+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+        <!-- API 11 theme customizations can go here. -->
+    </style>
+
+</resources>
diff --git a/bttestapp/res/values-v14/styles.xml b/bttestapp/res/values-v14/styles.xml
new file mode 100644
index 0000000..f316629
--- /dev/null
+++ b/bttestapp/res/values-v14/styles.xml
@@ -0,0 +1,41 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<resources>
+
+    <!--
+        Base application theme for API 14+. This theme completely replaces
+        AppBaseTheme from BOTH res/values/styles.xml and
+        res/values-v11/styles.xml on API 14+ devices.
+    -->
+    <style name="AppBaseTheme" parent="@android:style/Theme.DeviceDefault">
+        <!-- API 14 theme customizations can go here. -->
+    </style>
+
+</resources>
diff --git a/bttestapp/res/values/colors.xml b/bttestapp/res/values/colors.xml
new file mode 100644
index 0000000..4011f13
--- /dev/null
+++ b/bttestapp/res/values/colors.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<resources>
+    <!--
+        <color name="color_white">#ffffff</color>
+    -->
+
+    <color name="ind_on">#008000</color>
+    <color name="ind_off">#404040</color>
+    <color name="ind_text_unknown">#404040</color>
+    <color name="ind_bar_full">#008000</color>
+    <color name="ind_bar_medium">#804000</color>
+    <color name="ind_bar_low">#800000</color>
+    <color name="ind_bar_off">#404040</color>
+
+    <color name="call_unknown">#202020</color>
+    <color name="call_new">#000080</color>
+    <color name="call_held">#804000</color>
+    <color name="call_active">#008000</color>
+    <color name="call_selected">#50800000</color>
+
+</resources>
diff --git a/bttestapp/res/values/dimens.xml b/bttestapp/res/values/dimens.xml
new file mode 100644
index 0000000..5a78874
--- /dev/null
+++ b/bttestapp/res/values/dimens.xml
@@ -0,0 +1,36 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<resources>
+
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>
diff --git a/bttestapp/res/values/strings.xml b/bttestapp/res/values/strings.xml
new file mode 100644
index 0000000..cf8bcc0
--- /dev/null
+++ b/bttestapp/res/values/strings.xml
@@ -0,0 +1,330 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<resources>
+
+    <item type="id" name="dialpad_button" />
+    <item type="id" name="dialpad_button_pos" />
+
+<!--
+<string name=""></string>
+-->
+    <!-- General -->
+    <string name="app_name">BTTestApp</string>
+    <string name="blank"></string>
+    <string name="cancel">Cancel</string>
+
+    <string name="ppl_add">+</string>
+    <string name="ppl_del">-</string>
+
+    <!-- Main -->
+    <string name="services_header">Available services</string>
+    <string name="select_device">Select</string>
+    <string name="discover_services">Discover</string>
+    <string name="clear">Clear</string>
+    <string name="list_empty">No records found!</string>
+
+    <!-- Dialogs -->
+    <string name="dialog_title_create_msg_filter">Create message filter:</string>
+    <string name="dialog_title_pick_date_time">Pick date and time:</string>
+
+    <!-- Titles -->
+    <string name="title_hfp_test">HFP Test</string>
+    <string name="title_pbap_test">PBAP Test</string>
+    <string name="title_map_test">MAP Test</string>
+    <string name="title_vcard_filter">Filter Attributes</string>
+    <string name="title_message_filter">Select Message Parameters</string>
+
+    <!-- PBAP -->
+    <string name="phonebook_prompt">Choose a phonebook:</string>
+    <string name="vcard_format_prompt">Choose a vCard format:</string>
+    <string name="max_list_count_prompt">MaxListCount:</string>
+    <string name="vcard_header_prompt">vCard header:</string>
+    <string name="offset_prompt">Offset value:</string>
+    <string name="search">Search</string>
+    <string name="getsize">Get size</string>
+    <string name="abort">Abort</string>
+    <string name="pbap_session_key_dialog_header">OBEX session key required</string>
+    <string name="pbap_session_key_dialog_title">Type session key for %1$s</string>
+    <string name="pbap_authentication_timeout_message">There was time out to input session key with %1$s</string>
+    <string name="auth_notif_ticker">OBEX authentication request</string>
+    <string name="auth_notif_title">Session key</string>
+    <string name="auth_notif_message">Type session key for %1$s</string>
+    <string name="get_vcard">Get vCard</string>
+
+    <string name="search_attribute_prompt">Search Attribute:</string>
+    <string name="search_value_prompt">Search Value:</string>
+    <string name="search_attribute_name">Name</string>
+    <string name="search_attribute_number">Number</string>
+
+    <string name="order_attribute_unordered">Default</string>
+    <string name="order_attribute_alphabetical">Alphabetical</string>
+    <string name="order_attribute_indexed">Indexed</string>
+    <string name="order_attribute_phonetic">Phonetic</string>
+    <string name="search_attribute_sound">Sound</string>
+
+    <string name="filter_button">Select Filter Attributes</string>
+    <string name="filter_field_version">vCard Version</string>
+    <string name="filter_field_fn">Formatted Name</string>
+    <string name="filter_field_n">Structured Presentation of Name</string>
+    <string name="filter_field_photo">Associated Image or Photo</string>
+    <string name="filter_field_bday">Birthday</string>
+    <string name="filter_field_adr">Delivery Address</string>
+    <string name="filter_field_label">Delivery</string>
+    <string name="filter_field_tel">Telephone Number</string>
+    <string name="filter_field_email">Electronic Mail Address</string>
+    <string name="filter_field_mailer">Electronic Mail</string>
+    <string name="filter_field_tz">Time Zone</string>
+    <string name="filter_field_geo">Geographic Position</string>
+    <string name="filter_field_title">Job</string>
+    <string name="filter_field_role">Role within the Organization</string>
+    <string name="filter_field_logo">Organization Logo</string>
+    <string name="filter_field_agent">vCard of Person Representing</string>
+    <string name="filter_field_org">Name of Organization</string>
+    <string name="filter_field_note">Comments</string>
+    <string name="filter_field_rev">Revision</string>
+    <string name="filter_field_sound">Pronunciation of Name</string>
+    <string name="filter_field_url">Uniform Resource Locator</string>
+    <string name="filter_field_uid">Unique ID</string>
+    <string name="filter_field_key">Public Encryption Key</string>
+    <string name="filter_field_nickname">Nickname</string>
+    <string name="filter_field_categories">Categories</string>
+    <string name="filter_field_proid">Product ID</string>
+    <string name="filter_field_class">Class information</string>
+    <string name="filter_field_sort_string">String used for sorting operations</string>
+    <string name="filter_field_x_irmc_call_datetime">Time stamp</string>
+    <string name="filter_field_filter">Indicates the usage of a filter</string>
+
+    <string name="vcard21">vCard 2.1</string>
+    <string name="vcard30">vCard 3.0</string>
+
+    <string name="set_phonebook">SetPhonebook</string>
+
+    <!-- HLS -->
+
+    <!-- MAP -->
+    <string name="msgflag_text">TEXT</string>
+    <string name="msgflag_read">READ</string>
+    <string name="msgflag_sent">SENT</string>
+    <string name="msgflag_drm">DRM</string>
+    <string name="msgflag_prio">PRIO</string>
+    <string name="map_goto_inbox">go to inbox</string>
+    <string name="map_goto_outbox">go to outbox</string>
+    <string name="map_goto_draft">go to draft</string>
+    <string name="map_msg_copy_to_editor_lbl">Copy &gt;</string>
+    <string name="map_msg_push_lbl">Push message &gt;</string>
+    <string name="map_msg_push_transparent_lbl">transparent</string>
+    <string name="map_msg_push_retry_lbl">retry</string>
+    <string name="charset_native_lbl">native</string>
+    <string name="charset_utf8_lbl">UTF-8</string>
+    <string name="map_msg_get_lbl">Get message &gt;</string>
+    <string name="map_msg_get_attachment_lbl">w/attach</string>
+    <string name="map_folder_root">/</string>
+    <string name="map_folder_up">..</string>
+    <string name="map_folder_enter">-></string>
+    <string name="map_folder_listing">list</string>
+    <string name="map_folder_listing_size">size</string>
+    <string name="map_message_listing">Get Message Listing</string>
+    <string name="map_message_listing_size">Get Message Listing Size</string>
+    <string name="map_update_inbox">Update Inbox</string>
+    <string name="map_delete_message">Delete</string>
+    <string name="map_max_list_count_prompt">Max list count:</string>
+    <string name="map_list_start_offset_prompt">Start offset:</string>
+    <string name="map_subject_length_prompt">Subject length:</string>
+    <string name="map_parameter_subject">Subject</string>
+    <string name="map_parameter_datetime">Datetime</string>
+    <string name="map_parameter_sender_name">Sender Name</string>
+    <string name="map_parameter_sender_addressing">Sender Addressing</string>
+    <string name="map_parameter_recipient_name">Recipient Name</string>
+    <string name="map_parameter_recipient_addressing">Recipient Addressing</string>
+    <string name="map_parameter_type">Type</string>
+    <string name="map_parameter_size">Size</string>
+    <string name="map_parameter_reception_status">Reception Status</string>
+    <string name="map_parameter_text">Text</string>
+    <string name="map_parameter_attachment_size">Attachment Size</string>
+    <string name="map_parameter_priority">Priority</string>
+    <string name="map_parameter_read">Read</string>
+    <string name="map_parameter_sent">Sent</string>
+    <string name="map_parameter_protected">Protected</string>
+    <string name="map_parameter_replyto_addressing">Replayto Addressing</string>
+    <string name="map_message_parameters">Parameters</string>
+    <string name="map_message_filter">Filter</string>
+    <string name="map_msg_row_from">&lt;</string>
+    <string name="map_msg_row_to">&gt;</string>
+    <string name="map_msg_type_sms_gsm">SMS GSM</string>
+    <string name="map_msg_type_sms_cdma">SMS CDMA</string>
+    <string name="map_msg_type_email">EMAIL</string>
+    <string name="map_msg_type_mms">MMS</string>
+    <string name="map_filter_any_status">Any status</string>
+    <string name="map_filter_any_priority">Any priority</string>
+    <string name="map_filter_without_message_type">Filter out message type:</string>
+    <string name="map_filter_read_status">Read status:</string>
+    <string name="map_filter_read_status_unread">Unread</string>
+    <string name="map_filter_read_status_read">Read</string>
+    <string name="map_filter_priority">Priority:</string>
+    <string name="map_filter_priority_high">High</string>
+    <string name="map_filter_priority_non_high">Non-high</string>
+    <string name="map_filter_period_begin">Period begin:</string>
+    <string name="map_filter_period_end">Period end:</string>
+    <string name="map_filter_recipient">Recipient:</string>
+    <string name="map_filter_originator">Originator:</string>
+    <string name="map_messages_list_empty">No messages found!</string>
+    <string name="map_set_read">Set READ</string>
+    <string name="map_set_unread">Set UNREAD</string>
+    <string name="map_recipients_prompt">Recipients:</string>
+    <string name="map_originators_prompt">Originators:</string>
+    <string name="map_bmsg_status_prompt">Status:</string>
+    <string name="map_bmsg_type_prompt">Type:</string>
+    <string name="map_bmsg_folder_prompt">Folder:</string>
+    <string name="map_bbody_encoding_prompt">Encoding:</string>
+    <string name="map_bbody_charset_prompt">Charset:</string>
+    <string name="map_bbody_language_prompt">Language:</string>
+    <string name="map_report_notif_ticker">New MAP notification report received!</string>
+    <string name="map_report_notif_title_delivery_success">Delivery success</string>
+    <string name="map_report_notif_title_sending_success">Sending success</string>
+    <string name="map_report_notif_title_delivery_failure">Delivery failure</string>
+    <string name="map_report_notif_title_sending_failure">Sending failure</string>
+    <string name="map_report_notif_title_memory_full">Memory full</string>
+    <string name="map_report_notif_title_memory_available">Memory available</string>
+    <string name="map_report_notif_title_message_deleted">Message deleted</string>
+    <string name="map_report_notif_received">Received new %1$s message</string>
+    <string name="map_report_notif_handle">Handle: %1$s</string>
+    <string name="map_report_notif_shifted">Message %1$s shifted</string>
+    <string name="map_report_notif_fromto">From %1$s to %2$s</string>
+
+    <string-array name="map_message_type_array">
+        <item>SMS_GSM</item>
+        <item>SMS_CDMA</item>
+        <item>EMAIL</item>
+        <item>MMS</item>
+    </string-array>
+
+    <string-array name="map_message_encoding_array">
+        <item></item>
+        <item>8BIT</item>
+        <item>G-7BIT</item>
+        <item>G-7BITEXT</item>
+        <item>G-UCS2</item>
+        <item>G-8BIT</item>
+        <item>C-8BIT</item>
+        <item>C-EPM</item>
+        <item>C-7ASCII</item>
+        <item>C-IA5</item>
+        <item>C-UNICODE</item>
+        <item>C-SJIS</item>
+        <item>C-KOREAN</item>
+        <item>C-LATINHEB</item>
+        <item>C-LATIN</item>
+    </string-array>
+
+    <string-array name="phonebook_arrays">
+        <item>telecom/pb.vcf</item>
+        <item>telecom/ich.vcf</item>
+        <item>telecom/och.vcf</item>
+        <item>telecom/mch.vcf</item>
+        <item>telecom/cch.vcf</item>
+        <item>SIM1/telecom/pb.vcf</item>
+        <item>SIM1/telecom/ich.vcf</item>
+        <item>SIM1/telecom/och.vcf</item>
+        <item>SIM1/telecom/mch.vcf</item>
+        <item>SIM1/telecom/cch.vcf</item>
+    </string-array>
+
+    <!-- Contact Details -->
+    <string name="contact_phonelist_hdr">Phone list</string>
+    <string name="contact_emaillist_hdr">E-mail list</string>
+
+    <!-- Menu -->
+    <string name="menu_save">Save</string>
+    <string name="menu_connect">Connect</string>
+    <string name="menu_disconnect">Disconnect</string>
+
+    <!-- Other -->
+    <string name="vcard">vCard</string>
+    <string name="download">Download</string>
+    <string name="browse">Browse</string>
+
+    <!-- Toast messages -->
+    <string name="msg_connection_success">Device has been connected.</string>
+    <string name="msg_disconnection_success">Device has been disconnected.</string>
+
+
+    <!-- HFP test activity -->
+    <string name="hfptest_call_state_unknown_text">unknown</string>
+    <string name="menu_request_phone_num">Request phone number</string>
+    <string name="menu_call_history">Call history</string>
+    <string name="call_history_title">Select number:</string>
+    <string name="dialpad_number_hint">Click &quot;<sup><small>Re</small></sup>Dial&quot; to redial</string>
+    <string name="dialpad_del">Delete</string>
+    <string name="dialpad_dial"><sup><small>Re</small></sup>Dial</string>
+    <string name="dialpad_memdial">Mem dial</string>
+    <string name="dialpad_dtmf">DTMF</string>
+    <string name="ind_conn_state">CONN</string>
+    <string name="ind_audio_state">AUDIO</string>
+    <string name="ind_vr_state">VR</string>
+    <string name="ind_inband_state">IN-BAND</string>
+    <string name="ind_network_state">NETWORK</string>
+    <string name="ind_roaming_state">ROAMING</string>
+    <string name="ind_signal_level">signal</string>
+    <string name="ind_battery_level">battery</string>
+    <string name="ind_operator_unknown">no operator info</string>
+    <string name="ind_subscriber_unknown">no subscriber info</string>
+    <string name="ind_bar">&#x258c;&#x258c;&#x258c;&#x258c;&#x258c;</string>
+    <string name="call_state_active">ACTIVE</string>
+    <string name="call_state_held">HELD</string>
+    <string name="call_state_dialing">DIALING</string>
+    <string name="call_state_alerting">ALERTING</string>
+    <string name="call_state_incoming">INCOMING</string>
+    <string name="call_state_waiting">WAITING</string>
+    <string name="call_state_held_by_rnh">R&amp;H</string>
+    <string name="call_multiparty">MULTIPARTY</string>
+    <string name="call_action_accept">Accept</string>
+    <string name="call_action_accept_held">Accept held</string>
+    <string name="call_action_merge">Merge calls</string>
+    <string name="call_action_hold_and_accept">Hold and accept</string>
+    <string name="call_action_swap">Swap calls</string>
+    <string name="call_action_release_and_accept">Release and accept</string>
+    <string name="call_action_reject">Reject</string>
+    <string name="call_action_reject_held">Reject held</string>
+    <string name="call_action_terminate">Terminate</string>
+    <string name="call_action_hold">Hold</string>
+    <string name="call_action_respond_and_hold">Respond and hold</string>
+    <string name="call_action_private_mode">Private mode</string>
+    <string name="call_action_explicit_transfer">Explicit transfer</string>
+
+        <!-- HFP result codes messages -->
+    <string name="hfptest_result_ok_text">OK</string>
+    <string name="hfptest_result_error_text">Error</string>
+    <string name="hfptest_result_no_carrier_text">Error: no carrier</string>
+    <string name="hfptest_result_busy_text">Error: busy</string>
+    <string name="hfptest_result_no_answer_text">Error: no answar</string>
+    <string name="hfptest_result_delayed_text">Error: delayed</string>
+    <string name="hfptest_result_blacklisted_text">Error: blacklisted</string>
+    <string name="hfptest_result_cme_text">Error: CME</string>
+</resources>
diff --git a/bttestapp/res/values/styles.xml b/bttestapp/res/values/styles.xml
new file mode 100644
index 0000000..00ea0d5
--- /dev/null
+++ b/bttestapp/res/values/styles.xml
@@ -0,0 +1,78 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<resources>
+
+    <!--
+        Base application theme, dependent on API level. This theme is replaced
+        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Light">
+        <!--
+            Theme customizations available in newer API levels can go in
+            res/values-vXX/styles.xml, while customizations related to
+            backward-compatibility can go here.
+        -->
+    </style>
+
+    <!-- Application theme. -->
+    <style name="AppTheme" parent="AppBaseTheme">
+        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+    </style>
+
+    <style name="TextViewAsLabel">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:background">#525252</item>
+        <item name="android:textColor">#c0c0c0</item>
+        <item name="android:layout_marginTop">3dp</item>
+        <item name="android:textStyle">bold</item>
+    </style>
+
+    <style name="somePadding">
+        <item name="android:paddingLeft">20dp</item>
+        <item name="android:paddingRight">20dp</item>
+    </style>
+
+    <style name="someSmallPadding">
+        <item name="android:paddingLeft">10dp</item>
+        <item name="android:paddingRight">10dp</item>
+        <item name="android:paddingTop">2dp</item>
+        <item name="android:paddingBottom">2dp</item>
+    </style>
+
+    <style name="indicator">
+        <item name="android:layout_marginTop">5dp</item>
+        <item name="android:layout_marginBottom">5dp</item>
+        <item name="android:layout_marginLeft">5dp</item>
+        <item name="android:layout_marginRight">5dp</item>
+        <item name="android:gravity">center</item>
+    </style>
+
+</resources>
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ActivityHelper.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ActivityHelper.java
new file mode 100644
index 0000000..f91ae3d
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ActivityHelper.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.content.pm.ActivityInfo;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+
+public class ActivityHelper {
+    public static void initialize(Activity activity, int layout) {
+        // Do all sorts of common task for your activities here including:
+        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
+        activity.setContentView(layout);
+        activity.getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
+        activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME
+                | ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM);
+        activity.getWindow().setSoftInputMode(
+                WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
+    }
+
+    public static void setActionBarTitle(Activity activity, int title) {
+        activity.getActionBar().setTitle(title);
+    }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/BluetoothConnectionReceiver.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/BluetoothConnectionReceiver.java
new file mode 100644
index 0000000..0f276c8
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/BluetoothConnectionReceiver.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import org.codeaurora.bluetooth.bttestapp.util.Logger;
+
+import java.util.ArrayList;
+
+public class BluetoothConnectionReceiver extends BroadcastReceiver {
+
+    private final static String TAG = "BluetoothConnectionReceiver";
+
+    public static final String ACTION_NEW_BLUETOOTH_DEVICE = "com.qualcomm.bluetooth.action.NEW_BLUETOOTH_DEVICE";
+
+    public static final String EXTRA_DEVICE_ADDRESS = "com.qualcomm.bluetooth.extra.DEVICE_ADDRESS";
+
+    private static ArrayList<IBluetoothConnectionObserver> observers = new ArrayList<IBluetoothConnectionObserver>();
+
+    private static BluetoothDevice selectedDevice = null;
+
+    public static void registerObserver(IBluetoothConnectionObserver observer) {
+        observers.add(observer);
+
+        if (selectedDevice != null) {
+            observer.onDeviceChanged(selectedDevice);
+        }
+    }
+
+    public static void removeObserver(IBluetoothConnectionObserver observer) {
+        observers.remove(observer);
+    }
+
+    private void notifyObserversDeviceChanged(BluetoothDevice device) {
+        for (IBluetoothConnectionObserver observer : observers) {
+            observer.onDeviceChanged(device);
+        }
+    }
+
+    private void notifyObserversDeviceDisconected() {
+        for (IBluetoothConnectionObserver observer : observers) {
+            observer.onDeviceDisconected();
+        }
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (ACTION_NEW_BLUETOOTH_DEVICE.equals(intent.getAction())) {
+            Logger.v(TAG, "Receive new bluetooth device.");
+
+            String address = intent.getStringExtra(EXTRA_DEVICE_ADDRESS);
+
+            if (address != null) {
+                BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+                selectedDevice = adapter.getRemoteDevice(address);
+                notifyObserversDeviceChanged(selectedDevice);
+            } else {
+                Logger.e(TAG, "Received NULL address!");
+            }
+        } else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(intent.getAction())) {
+            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+            if (device.equals(selectedDevice)) {
+                Logger.v(TAG, "Received bluetooth disconected.");
+
+                notifyObserversDeviceDisconected();
+            }
+        } else {
+            Logger.w(TAG, "Unknown intent received with action: " + intent.getAction());
+        }
+    }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/CallHistoryDialogFragment.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/CallHistoryDialogFragment.java
new file mode 100644
index 0000000..8e1cda8
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/CallHistoryDialogFragment.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import org.codeaurora.bluetooth.bttestapp.R;
+
+import java.util.ArrayList;
+
+class CallHistoryDialogFragment extends DialogFragment {
+
+    private final ArrayList<String> mElements;
+
+    private CallHistoryDialogListener mListener;
+
+    public interface CallHistoryDialogListener {
+        public void onCallHistoryDialogPositive(DialogFragment dialog, String number);
+    };
+
+    public CallHistoryDialogFragment(ArrayList<String> elements) {
+        super();
+
+        mElements = new ArrayList<String>(elements);
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+        CharSequence[] elements = mElements.toArray(new CharSequence[mElements.size()]);
+
+        builder.setTitle(R.string.call_history_title);
+        builder.setItems(elements, new DialogInterface.OnClickListener() {
+
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                mListener.onCallHistoryDialogPositive(CallHistoryDialogFragment.this,
+                        mElements.get(which));
+            }
+        });
+        builder.setNegativeButton(android.R.string.cancel, null);
+
+        return builder.create();
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+
+        try {
+            mListener = (CallHistoryDialogListener) activity;
+        } catch (ClassCastException e) {
+            throw new ClassCastException(activity.toString() + " must implement "
+                    + CallHistoryDialogListener.class.getSimpleName());
+        }
+    }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/CallsListFragment.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/CallsListFragment.java
new file mode 100644
index 0000000..9c87abd
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/CallsListFragment.java
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*package org.codeaurora.bttestapp;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHandsfreeClient;
+import android.bluetooth.BluetoothHandsfreeClientCall;
+import android.bluetooth.BluetoothProfile;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.qualcomm.bttestapp.R;
+
+public class CallsListFragment extends Fragment implements OnClickListener, OnItemClickListener {
+
+    private final static String TAG = "CallsListFragment";
+
+    private HfpTestActivity mActivity;
+
+    private ListView mCallsList;
+
+    private Button mActionAccept;
+
+    private Button mActionHoldAndAccept;
+
+    private Button mActionReleaseAndAccept;
+
+    private Button mActionReject;
+
+    private Button mActionTerminate;
+
+    private Button mActionHold;
+
+    private Button mActionRespondAndHold;
+
+    private Button mActionPrivateMode;
+
+    private Button mActionExplicitTransfer;
+
+    private class CallsAdapter extends BaseAdapter {
+
+        private final SparseArray<BluetoothHandsfreeClientCall> mCalls;
+
+        private int mSelectedId = 0;
+
+        CallsAdapter() {
+            mCalls = new SparseArray<BluetoothHandsfreeClientCall>();
+        }
+
+        @Override
+        public int getCount() {
+            return mCalls.size();
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return mCalls.get(mCalls.keyAt(position));
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return mCalls.keyAt(position);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            View view = convertView;
+
+            BluetoothHandsfreeClientCall call = (BluetoothHandsfreeClientCall) getItem(position);
+
+            if (view == null) {
+                view = getActivity().getLayoutInflater().inflate(R.layout.call_view, parent, false);
+            }
+
+            if (mSelectedId == call.getId()) {
+                view.setBackgroundResource(R.drawable.selected_call_bg);
+            } else {
+                view.setBackgroundResource(0);
+            }
+
+            TextView callId = (TextView) view.findViewById(R.id.call_id);
+            callId.setText(Integer.toString(call.getId()));
+
+            ImageView dir = (ImageView) view.findViewById(R.id.call_direction);
+            dir.setImageResource(call.isOutgoing() ? R.drawable.ic_call_outgoing_holo_dark
+                    : R.drawable.ic_call_incoming_holo_dark);
+
+            ((TextView) view.findViewById(R.id.call_number)).setText(call.getNumber());
+
+            switch (call.getState()) {
+                case BluetoothHandsfreeClientCall.CALL_STATE_ACTIVE:
+                    callId.setBackgroundColor(getColor(R.color.call_active));
+                    break;
+
+                case BluetoothHandsfreeClientCall.CALL_STATE_HELD:
+                case BluetoothHandsfreeClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD:
+                    callId.setBackgroundColor(getColor(R.color.call_held));
+                    break;
+
+                case BluetoothHandsfreeClientCall.CALL_STATE_DIALING:
+                case BluetoothHandsfreeClientCall.CALL_STATE_ALERTING:
+                case BluetoothHandsfreeClientCall.CALL_STATE_INCOMING:
+                case BluetoothHandsfreeClientCall.CALL_STATE_WAITING:
+                    callId.setBackgroundColor(getColor(R.color.call_new));
+                    break;
+
+                default:
+                    callId.setBackgroundColor(getColor(R.color.call_unknown));
+                    break;
+            }
+
+            setIndicator(view, R.id.call_state_active,
+                    call.getState() == BluetoothHandsfreeClientCall.CALL_STATE_ACTIVE);
+            setIndicator(view, R.id.call_state_held,
+                    call.getState() == BluetoothHandsfreeClientCall.CALL_STATE_HELD);
+            setIndicator(view, R.id.call_state_dialing,
+                    call.getState() == BluetoothHandsfreeClientCall.CALL_STATE_DIALING);
+            setIndicator(view, R.id.call_state_alerting,
+                    call.getState() == BluetoothHandsfreeClientCall.CALL_STATE_ALERTING);
+            setIndicator(view, R.id.call_state_incoming,
+                    call.getState() == BluetoothHandsfreeClientCall.CALL_STATE_INCOMING);
+            setIndicator(view, R.id.call_state_waiting,
+                    call.getState() == BluetoothHandsfreeClientCall.CALL_STATE_WAITING);
+            setIndicator(
+                    view,
+                    R.id.call_state_held_by_rnh,
+                    call.getState() == BluetoothHandsfreeClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD);
+
+            setIndicator(view, R.id.call_multiparty, call.isMultiParty());
+
+            return view;
+        }
+
+        public void add(BluetoothHandsfreeClientCall call) {
+            mCalls.put(call.getId(), call);
+            notifyDataSetChanged();
+        }
+
+        public void remove(BluetoothHandsfreeClientCall call) {
+            if (call.getId() == mSelectedId) {
+                mSelectedId = 0;
+            }
+            mCalls.remove(call.getId());
+            notifyDataSetChanged();
+        }
+
+        public void removeAll() {
+            mCalls.clear();
+            notifyDataSetChanged();
+        }
+
+        public void toggleSelected(int id) {
+            if (id == mSelectedId) {
+                mSelectedId = 0;
+            } else {
+                mSelectedId = id;
+            }
+            notifyDataSetChanged();
+        }
+
+        public BluetoothHandsfreeClientCall getSelected() {
+            return mCalls.get(mSelectedId);
+        }
+
+        public boolean hasCallsInState(int... state) {
+            for (int idx = 0; idx < mCalls.size(); idx++) {
+                BluetoothHandsfreeClientCall call = mCalls.valueAt(idx);
+                for (int s : state) {
+                    if (call.getState() == s) {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+
+        private void setIndicator(View view, int id, boolean state) {
+            TextView txt = (TextView) view.findViewById(id);
+            txt.setVisibility(state ? View.VISIBLE : View.INVISIBLE);
+        }
+
+        private int getColor(int id) {
+            return getResources().getColor(id);
+        }
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.calls_list_fragment, null);
+
+        mCallsList = (ListView) view.findViewById(R.id.calls_list);
+        mCallsList.setAdapter(new CallsAdapter());
+        mCallsList.setOnItemClickListener(this);
+
+        mActionAccept = (Button) view.findViewById(R.id.call_action_accept);
+        mActionHoldAndAccept = (Button) view.findViewById(R.id.call_action_hold_and_accept);
+        mActionReleaseAndAccept = (Button) view.findViewById(R.id.call_action_release_and_accept);
+        mActionReject = (Button) view.findViewById(R.id.call_action_reject);
+        mActionTerminate = (Button) view.findViewById(R.id.call_action_terminate);
+        mActionHold = (Button) view.findViewById(R.id.call_action_hold);
+        mActionRespondAndHold = (Button) view.findViewById(R.id.call_action_respond_and_hold);
+        mActionPrivateMode = (Button) view.findViewById(R.id.call_action_private_mode);
+        mActionExplicitTransfer = (Button) view.findViewById(R.id.call_action_explicit_transfer);
+
+        mActionAccept.setEnabled(false);
+        mActionHoldAndAccept.setEnabled(false);
+        mActionReleaseAndAccept.setEnabled(false);
+        mActionReject.setEnabled(false);
+        mActionTerminate.setEnabled(false);
+        mActionHold.setEnabled(false);
+        mActionRespondAndHold.setEnabled(false);
+        mActionPrivateMode.setEnabled(false);
+        mActionExplicitTransfer.setEnabled(false);
+
+        mActionAccept.setOnClickListener(this);
+        mActionHoldAndAccept.setOnClickListener(this);
+        mActionReleaseAndAccept.setOnClickListener(this);
+        mActionReject.setOnClickListener(this);
+        mActionTerminate.setOnClickListener(this);
+        mActionHold.setOnClickListener(this);
+        mActionRespondAndHold.setOnClickListener(this);
+        mActionPrivateMode.setOnClickListener(this);
+        mActionExplicitTransfer.setOnClickListener(this);
+
+        return view;
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+
+        try {
+            mActivity = (HfpTestActivity) activity;
+        } catch (ClassCastException e) {
+            throw new ClassCastException(this.getClass().getSimpleName()
+                    + " can be only attached to " + HfpTestActivity.class.getSimpleName());
+        }
+    }
+
+    public void onCallChanged(BluetoothHandsfreeClientCall call) {
+        Log.v(TAG, "onCallChanged(): call=" + HfpTestActivity.callToJson(call));
+
+        CallsAdapter adapter = (CallsAdapter) mCallsList.getAdapter();
+
+        if (call.getState() == BluetoothHandsfreeClientCall.CALL_STATE_TERMINATED) {
+            adapter.remove(call);
+        } else {
+            // add will also update existing call with the same id
+            adapter.add(call);
+        }
+
+        updateActionsState();
+    }
+
+    @Override
+    public void onClick(View v) {
+        BluetoothHandsfreeClient cli = mActivity.mBluetoothHandsfreeClient;
+        BluetoothDevice device = mActivity.mDevice;
+        CallsAdapter adapter = (CallsAdapter) mCallsList.getAdapter();
+        BluetoothHandsfreeClientCall selectedCall = adapter.getSelected();
+        boolean result = true;
+
+        switch (v.getId()) {
+            case R.id.call_action_accept:
+                result = cli.acceptCall(device, BluetoothHandsfreeClient.CALL_ACCEPT_NONE);
+                break;
+
+            case R.id.call_action_hold_and_accept:
+                result = cli.acceptCall(device, BluetoothHandsfreeClient.CALL_ACCEPT_HOLD);
+                break;
+
+            case R.id.call_action_release_and_accept:
+                result = cli.acceptCall(device, BluetoothHandsfreeClient.CALL_ACCEPT_TERMINATE);
+                break;
+
+            case R.id.call_action_reject:
+                result = cli.rejectCall(device);
+                break;
+
+            case R.id.call_action_terminate:
+                result = cli.terminateCall(device, selectedCall == null ? 0 : selectedCall.getId());
+                break;
+
+            case R.id.call_action_hold:
+                result = cli.holdCall(device);
+                break;
+
+            case R.id.call_action_respond_and_hold:
+                result = cli.holdCall(device);
+                break;
+
+            case R.id.call_action_private_mode:
+                result = cli.enterPrivateMode(device, selectedCall.getId());
+                break;
+
+            case R.id.call_action_explicit_transfer:
+                result = cli.explicitCallTransfer(device);
+                break;
+        }
+
+        if (!result) {
+            Toast.makeText(getActivity(), "\"" + ((TextView) v).getText() + "\" FAILED",
+                    Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    @Override
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        ((CallsAdapter) parent.getAdapter()).toggleSelected((int) id);
+
+        updateActionsState();
+    }
+
+    public void onConnStateChanged(int state, int prevState) {
+        if (state == BluetoothProfile.STATE_DISCONNECTED) {
+            ((CallsAdapter) mCallsList.getAdapter()).removeAll();
+
+            updateActionsState();
+        }
+    }
+
+    private void updateActionsState() {
+        CallsAdapter adapter = (CallsAdapter) mCallsList.getAdapter();
+        BluetoothHandsfreeClientCall selectedCall = adapter.getSelected();
+
+        boolean canAccept = false;
+        boolean canHoldAndAccept = false;
+        boolean canReleaseAndAccept = false;
+        boolean canReject = false;
+        boolean canTerminate = false;
+        boolean canRespondAndHold = false;
+        boolean canHold = false;
+
+        boolean hasIncoming = adapter.hasCallsInState(BluetoothHandsfreeClientCall.CALL_STATE_INCOMING);
+        boolean hasActive = adapter.hasCallsInState(BluetoothHandsfreeClientCall.CALL_STATE_ACTIVE);
+        boolean hasWaiting = adapter.hasCallsInState(BluetoothHandsfreeClientCall.CALL_STATE_WAITING);
+        boolean hasHeld = adapter.hasCallsInState(BluetoothHandsfreeClientCall.CALL_STATE_HELD,
+                BluetoothHandsfreeClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD);
+
+        if (hasIncoming) {
+            canAccept = true;
+            canReject = mActivity.mFeatReject;
+            canRespondAndHold = true;
+
+            mActionAccept.setText(R.string.call_action_accept);
+            mActionReject.setText(R.string.call_action_reject);
+        }
+
+        if (hasActive || adapter.hasCallsInState(BluetoothHandsfreeClientCall.CALL_STATE_ALERTING,
+                                                BluetoothHandsfreeClientCall.CALL_STATE_DIALING)) {
+            canTerminate = true;
+        }
+
+        if (hasHeld) {
+            canAccept = true;
+            canReject = mActivity.mFeatReject;
+            canHoldAndAccept = mActivity.mFeatAcceptHeldOrWaiting;
+        }
+
+        if (hasWaiting) {
+            canReject = mActivity.mFeatReject;
+            mActionAccept.setText(R.string.call_action_accept);
+            mActionReject.setText(R.string.call_action_reject);
+
+            if (hasActive) {
+                canHoldAndAccept = mActivity.mFeatAcceptHeldOrWaiting;
+                canReleaseAndAccept = mActivity.mFeatReleaseAndAccept;
+
+                mActionHoldAndAccept.setText(R.string.call_action_hold_and_accept);
+            } else if (hasHeld) {
+                canAccept = true;
+                canHoldAndAccept = false;
+                canReleaseAndAccept = false;
+            }
+        }
+
+        if (adapter.hasCallsInState(BluetoothHandsfreeClientCall.CALL_STATE_ACTIVE) &&
+                !adapter.hasCallsInState(BluetoothHandsfreeClientCall.CALL_STATE_HELD,
+                        BluetoothHandsfreeClientCall.CALL_STATE_DIALING,
+                        BluetoothHandsfreeClientCall.CALL_STATE_ALERTING,
+                        BluetoothHandsfreeClientCall.CALL_STATE_INCOMING,
+                        BluetoothHandsfreeClientCall.CALL_STATE_WAITING,
+                        BluetoothHandsfreeClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD)) {
+            canHold = true;
+        }
+
+        // TODO: this should be perhaps done in some more reasonable way
+        if (hasHeld && !hasWaiting) {
+            if (hasActive) {
+                canAccept = true;
+                canReleaseAndAccept = mActivity.mFeatReleaseAndAccept;
+
+                mActionAccept.setText(R.string.call_action_merge);
+                mActionHoldAndAccept.setText(R.string.call_action_swap);
+                mActionReject.setText(R.string.call_action_reject_held);
+            } else {
+                canAccept = false;
+                mActionAccept.setText(R.string.call_action_accept);
+                mActionHoldAndAccept.setText(R.string.call_action_accept_held);
+                mActionReject.setText(R.string.call_action_reject);
+                // Actions for incoming call when there is already a held one.
+                // We cannot accept the held one now. Instead handle the incoming call first.
+                if (hasIncoming) {
+                    canAccept = true;
+                    canHoldAndAccept = false;
+                }
+            }
+        } else {
+            mActionHoldAndAccept.setText(R.string.call_action_hold_and_accept);
+        }
+
+        mActionAccept.setEnabled(canAccept);
+        mActionHoldAndAccept.setEnabled(canHoldAndAccept);
+        mActionReleaseAndAccept.setEnabled(canReleaseAndAccept);
+        mActionReject.setEnabled(canReject);
+        mActionTerminate.setEnabled(canTerminate);
+        mActionRespondAndHold.setEnabled(canRespondAndHold);
+
+        mActionHold.setEnabled(canHold);
+
+        mActionPrivateMode.setEnabled(mActivity.mFeatEnhancedCallControl && selectedCall != null && selectedCall.isMultiParty());
+
+        mActionExplicitTransfer.setEnabled(mActivity.mFeatMergeDetach && (adapter.getCount() > 1));
+    }
+}*/
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/DialpadFragment.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/DialpadFragment.java
new file mode 100644
index 0000000..3e5aa15
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/DialpadFragment.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*package org.codeaurora.bttestapp;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
+import android.widget.AbsListView.LayoutParams;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.EditText;
+import android.widget.GridView;
+import android.widget.ImageButton;
+import android.widget.Toast;
+import android.widget.ToggleButton;
+
+import com.qualcomm.bttestapp.R;
+
+public class DialpadFragment extends Fragment implements OnClickListener, OnLongClickListener {
+
+    private HfpTestActivity mActivity;
+
+    private GridView mButtonsGrid;
+
+    private EditText mNumberEdit;
+
+    private ToggleButton mDtmfButton;
+
+    private class ButtonAdapter extends BaseAdapter {
+
+        private final String[] mButtons = {
+                "1", "2", "3", "A",
+                "4", "5", "6", "B",
+                "7", "8", "9", "C",
+                "*", "0", "#", "D"
+        };
+
+        private final String[] mButtonsShifted = {
+                " ", " ", " ", " ",
+                " ", " ", " ", " ",
+                " ", " ", " ", " ",
+                " ", "+", " ", " "
+        };
+
+        private final Context mContext;
+
+        ButtonAdapter(Context context) {
+            mContext = context;
+        }
+
+        @Override
+        public int getCount() {
+            return mButtons.length;
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return new Pair<String, String>(mButtons[position], mButtonsShifted[position]);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            Button btn;
+
+            if (convertView == null) {
+                btn = new Button(mContext);
+                btn.setLayoutParams(new LayoutParams(
+                        android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+                        android.view.ViewGroup.LayoutParams.WRAP_CONTENT));
+                btn.setOnClickListener(DialpadFragment.this);
+                btn.setOnLongClickListener(DialpadFragment.this);
+            } else {
+                btn = (Button) convertView;
+            }
+
+            @SuppressWarnings("unchecked")
+            Pair<String, String> item = (Pair<String, String>) getItem(position);
+
+            btn.setText(item.first);
+            btn.setId(R.id.dialpad_button);
+            btn.setTag(item.first);
+            btn.setTag(R.id.dialpad_button_pos, position);
+
+            return btn;
+        }
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        Context context = getActivity();
+
+        View view = inflater.inflate(R.layout.dialpad_fragment, null);
+
+        mNumberEdit = (EditText) view.findViewById(R.id.dialpad_number);
+        mNumberEdit.setOnLongClickListener(this);
+
+        mButtonsGrid = (GridView) view.findViewById(R.id.dialpad);
+        mButtonsGrid.setAdapter(new ButtonAdapter(context));
+
+        ((ImageButton) view.findViewById(R.id.dialpad_del)).setOnClickListener(this);
+        ((ImageButton) view.findViewById(R.id.dialpad_del)).setOnLongClickListener(this);
+        ((Button) view.findViewById(R.id.dialpad_dial)).setOnClickListener(this);
+        ((Button) view.findViewById(R.id.dialpad_memdial)).setOnClickListener(this);
+
+        mDtmfButton = (ToggleButton) view.findViewById(R.id.dialpad_dtmf);
+        mDtmfButton.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                getView().findViewById(R.id.dialpad_number).setEnabled(!isChecked);
+                getView().findViewById(R.id.dialpad_del).setEnabled(!isChecked);
+                getView().findViewById(R.id.dialpad_dial).setEnabled(!isChecked);
+                getView().findViewById(R.id.dialpad_memdial).setEnabled(!isChecked);
+            }
+        });
+
+        return view;
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+
+        try {
+            mActivity = (HfpTestActivity) activity;
+        } catch (ClassCastException e) {
+            throw new ClassCastException(this.getClass().getSimpleName()
+                    + " can be only attached to " + HfpTestActivity.class.getSimpleName());
+        }
+    }
+
+    @Override
+    public void onClick(View view) {
+        switch (view.getId()) {
+            case R.id.dialpad_button:
+                int pos = (Integer) view.getTag(R.id.dialpad_button_pos);
+                onClickDialpadButton(pos, false);
+                break;
+
+            case R.id.dialpad_del:
+                onClickDelete();
+                break;
+
+            case R.id.dialpad_dial:
+                onClickDial();
+                break;
+
+            case R.id.dialpad_memdial:
+                onClickMemDial();
+                break;
+        }
+    }
+
+    @Override
+    public boolean onLongClick(View view) {
+        switch (view.getId()) {
+            case R.id.dialpad_button:
+                int pos = (Integer) view.getTag(R.id.dialpad_button_pos);
+                onClickDialpadButton(pos, true);
+                return true;
+
+            case R.id.dialpad_del:
+                mNumberEdit.setText("");
+                return true;
+
+            case R.id.dialpad_number:
+                onClickDialpadNumber();
+                return true;
+        }
+
+        return false;
+    }
+
+    private void onClickDialpadNumber() {
+        ClipboardManager cm = (ClipboardManager) mNumberEdit.getContext()
+                .getSystemService(Context.CLIPBOARD_SERVICE);
+
+        String txt = mNumberEdit.getText().toString();
+
+        if (txt.isEmpty()) {
+            ClipData clipData = cm.getPrimaryClip();
+            if (clipData != null) {
+                ClipData.Item item = clipData.getItemAt(0);
+                mNumberEdit.setText(item.getText());
+            }
+        } else {
+            cm.setPrimaryClip(ClipData.newPlainText(txt, txt));
+
+            Toast.makeText(mNumberEdit.getContext(), "copied: " +
+                    txt, Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    private void onClickDialpadButton(int position, boolean shift) {
+        @SuppressWarnings("unchecked")
+        Pair<String, String> bval = (Pair<String, String>) mButtonsGrid.getItemAtPosition(position);
+
+        if (mDtmfButton.isChecked()) {
+            mActivity.mBluetoothHandsfreeClient.sendDTMF(mActivity.mDevice,
+                    bval.first.getBytes()[0]);
+        } else {
+            if (shift) {
+                mNumberEdit.append(bval.second.trim());
+            } else {
+                mNumberEdit.append(bval.first.trim());
+            }
+        }
+    }
+
+    private void onClickDelete() {
+        String txt = mNumberEdit.getText().toString();
+
+        if (txt.length() > 0) {
+            txt = txt.substring(0, txt.length() - 1);
+            mNumberEdit.setText(txt);
+        }
+    }
+
+    private void onClickDial() {
+        String number = mNumberEdit.getText().toString().trim();
+
+        if (number.isEmpty()) {
+            mActivity.mBluetoothHandsfreeClient.redial(mActivity.mDevice);
+        } else {
+            mActivity.mBluetoothHandsfreeClient.dial(mActivity.mDevice, mNumberEdit.getText()
+                    .toString());
+        }
+    }
+
+    private void onClickMemDial() {
+        try {
+            mActivity.mBluetoothHandsfreeClient.dialMemory(mActivity.mDevice,
+                    Integer.valueOf(mNumberEdit.getText().toString()));
+        } catch (NumberFormatException e) {
+            // just ignore
+        }
+    }
+
+    public void setNumber(String number) {
+        mNumberEdit.setText(number);
+    }
+}*/
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/FilterActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/FilterActivity.java
new file mode 100644
index 0000000..4c4eb09
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/FilterActivity.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.CheckBox;
+
+import org.codeaurora.bluetooth.bttestapp.R;
+import org.codeaurora.bluetooth.bttestapp.util.Logger;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public abstract class FilterActivity extends Activity {
+
+    private final String TAG = "FilterActivity";
+
+    protected Map<Integer, Integer> mParameters = new HashMap<Integer, Integer>();
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+
+        long tmp = getIntent().getLongExtra("filter", 0);
+
+        fillParameters();
+
+        for (int i = 0; i < 64; i++) {
+            if ((tmp & 0x01) == 0x01) {
+                Integer id = mParameters.get(i);
+
+                if (id != null) {
+                    check(id);
+                }
+            }
+
+            tmp = (tmp >> 1);
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.menu_filter, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.menu_save:
+                Intent returnIntent = new Intent();
+                returnIntent.putExtra("result", getSelectedValue());
+                setResult(RESULT_OK, returnIntent);
+                finish();
+                break;
+            default:
+                Logger.w(TAG, "Unknown item selected.");
+                break;
+        }
+        return true;
+    }
+
+    protected void check(int id) {
+        setChecked(id, true);
+    }
+
+    protected Boolean isChecked(int id) {
+        CheckBox checkbox = (CheckBox) findViewById(id);
+        if (checkbox != null) {
+            return checkbox.isChecked();
+        }
+
+        Logger.w(TAG, "Check box " + id + " not found.");
+        return false;
+    }
+
+    protected void setChecked(int id, Boolean check) {
+        CheckBox checkbox = (CheckBox) findViewById(id);
+        if (checkbox != null) {
+            checkbox.setChecked(check);
+        } else {
+            Logger.w(TAG, "Check box " + id + " not found.");
+        }
+    }
+
+    protected long getSelectedValue() {
+        long result = 0;
+
+        Iterator<Entry<Integer, Integer>> it = mParameters.entrySet().iterator();
+
+        while (it.hasNext()) {
+            Entry<Integer, Integer> pair = it.next();
+
+            Integer key = pair.getKey();
+            Integer id = pair.getValue();
+
+            if (isChecked(id)) {
+                result |= ((long) 1 << key);
+            }
+        }
+
+        return result;
+    }
+
+    protected void addParameter(Integer byteNumber, Integer id) {
+        mParameters.put(byteNumber, id);
+    }
+
+    abstract protected void fillParameters();
+
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/GetTextDialogFragment.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/GetTextDialogFragment.java
new file mode 100644
index 0000000..475ef02
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/GetTextDialogFragment.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.widget.EditText;
+
+import org.codeaurora.bluetooth.bttestapp.R;
+
+class GetTextDialogFragment extends DialogFragment {
+
+    private GetTextDialogListener mListener;
+
+    public interface GetTextDialogListener {
+        public void onGetTextDialogPositive(DialogFragment dialog, String text);
+    };
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+        LayoutInflater inflater = getActivity().getLayoutInflater();
+
+        builder.setView(inflater.inflate(R.layout.string_picker_dialog, null))
+                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        EditText edt = (EditText) GetTextDialogFragment.this.getDialog()
+                                .findViewById(R.id.text);
+                        mListener.onGetTextDialogPositive(GetTextDialogFragment.this, edt.getText()
+                                .toString());
+                    }
+                })
+                .setNegativeButton(android.R.string.cancel, null);
+
+        return builder.create();
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+
+        try {
+            mListener = (GetTextDialogListener) activity;
+        } catch (ClassCastException e) {
+            throw new ClassCastException(activity.toString()
+                    + " must implement GetTextDialogListener");
+        }
+    }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/HfpTestActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/HfpTestActivity.java
new file mode 100644
index 0000000..d62d5c4
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/HfpTestActivity.java
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*package org.codeaurora.bttestapp;
+
+import android.app.ActionBar;
+import android.app.DialogFragment;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHandsfreeClient;
+import android.bluetooth.BluetoothHandsfreeClientCall;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import com.qualcomm.bttestapp.CallHistoryDialogFragment.CallHistoryDialogListener;
+import com.qualcomm.bttestapp.R;
+import com.qualcomm.bttestapp.util.Logger;
+import com.qualcomm.bttestapp.util.MonkeyEvent;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+
+public class HfpTestActivity extends MonkeyActivity implements IBluetoothConnectionObserver,
+        CallHistoryDialogListener {
+
+    private final String TAG = "HfpTestActivity";
+
+    private DialpadFragment mDialpadFragment = null;
+
+    private IndicatorsFragment mIndicatorsFragment = null;
+
+    private CallsListFragment mCallsListFragment = null;
+
+    private final ArrayList<String> mCallHistory = new ArrayList<String>();
+
+    private ActionBar mActionBar = null;
+
+   // /* this should be visible for fragments ///
+    BluetoothHandsfreeClient mBluetoothHandsfreeClient;
+    ProfileService mProfileService = null;
+    BluetoothDevice mDevice;
+
+    ///* this should be done in a more elegant way //
+    public boolean mFeatVtag;
+    public boolean mFeatVoiceRecognition;
+    public boolean mFeatThreeWayCalling;
+    public boolean mFeatEnhancedCallControl;
+
+    public boolean mFeatReject;
+    public boolean mFeatMerge;
+    public boolean mFeatMergeDetach;
+    public boolean mFeatReleaseAndAccept;
+    public boolean mFeatAcceptHeldOrWaiting;
+    public boolean mFeatReleaseHeldOrWaiting;
+
+    private final BroadcastReceiver mHfpClientReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            BluetoothDevice device = (BluetoothDevice)
+                    intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+            if (action.equals(BluetoothHandsfreeClient.ACTION_CONNECTION_STATE_CHANGED)) {
+
+                int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
+                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+                onReceiveActionConnectionStateChanged(device, prevState, state, intent.getExtras());
+                mCallsListFragment.onConnStateChanged(state, prevState);
+                mIndicatorsFragment.onConnStateChanged(state, prevState);
+
+                // Send MonkeyEvent
+                new MonkeyEvent("hfp-connection-state-changed", true)
+                        .addReplyParam("state", state)
+                        .addReplyParam("prevState", prevState)
+                        .send();
+            } else if (action.equals(BluetoothHandsfreeClient.ACTION_AG_EVENT)) {
+
+                mIndicatorsFragment.onAgEvent(intent.getExtras());
+
+            } else if (action.equals(BluetoothHandsfreeClient.ACTION_CALL_CHANGED)) {
+
+                BluetoothHandsfreeClientCall call = (BluetoothHandsfreeClientCall) intent.
+                        getParcelableExtra(BluetoothHandsfreeClient.EXTRA_CALL);
+                onCallChanged(call);
+                mCallsListFragment.onCallChanged(call);
+
+                // Send MonkeyEvent
+                new MonkeyEvent("hfp-call-changed", true)
+                        .addExtReply(callToJson(call))
+                        .send();
+            } else if (action.equals(BluetoothHandsfreeClient.ACTION_AUDIO_STATE_CHANGED)) {
+
+                int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
+                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+                onReceiveAudioStateChange(device, prevState, state);
+                mIndicatorsFragment.onAudioStateChanged(state, prevState);
+
+                // Send MonkeyEvent
+                new MonkeyEvent("hfp-audio-state-changed", true)
+                        .addReplyParam("state", state)
+                        .addReplyParam("prevState", prevState)
+                        .send();
+            } else if(action.equals(BluetoothHandsfreeClient.ACTION_RESULT)) {
+                int result = intent.getIntExtra(
+                        BluetoothHandsfreeClient.EXTRA_RESULT_CODE, -1);
+                int cme = intent.getIntExtra(
+                        BluetoothHandsfreeClient.EXTRA_CME_CODE, -1);
+
+                onReceiveResult(result, cme);
+
+                // Send MonkeyEvent
+                new MonkeyEvent("hfp-receive-result", true)
+                        .addReplyParam("result", result)
+                        .addReplyParam("cme", cme)
+                        .send();
+            } else if (action.equals(BluetoothHandsfreeClient.ACTION_LAST_VTAG)) {
+
+                String number = intent.getStringExtra(BluetoothHandsfreeClient.EXTRA_NUMBER);
+                Toast.makeText(HfpTestActivity.this, "Phone number received: " + number,
+                        Toast.LENGTH_LONG).show();
+                // Send MonkeyEvent
+                new MonkeyEvent("hfp-receive-phonenumber", true)
+                        .addReplyParam("number", number)
+                        .send();
+            }
+        }
+
+        private void onCallChanged(BluetoothHandsfreeClientCall call) {
+            int state = call.getState();
+            String number = call.getNumber().trim();
+
+            new MonkeyEvent("hfp-call-changed", true).addExtReply(callToJson(call)).send();
+
+            if (number.isEmpty()) {
+                return;
+            }
+
+            /// we add any "new" call to list and put in at the beginning
+            if (state == BluetoothHandsfreeClientCall.CALL_STATE_ALERTING ||
+                    state == BluetoothHandsfreeClientCall.CALL_STATE_DIALING ||
+                    state == BluetoothHandsfreeClientCall.CALL_STATE_INCOMING ||
+                    state == BluetoothHandsfreeClientCall.CALL_STATE_WAITING) {
+                mCallHistory.remove(number);
+                mCallHistory.add(0, number);
+
+                if (mCallHistory.size() > 20) {
+                    mCallHistory.remove(20);
+                }
+            }
+        }
+
+        private void onReceiveResult(int result, int cme) {
+            Logger.v(TAG, "onReceiveResult (result: " + result + " cme: " +
+                    cme);
+
+            Toast.makeText(HfpTestActivity.this,
+                    resultCodeToString(result, cme),
+                    Toast.LENGTH_SHORT).show();
+        }
+
+        private synchronized void onReceiveAudioStateChange(BluetoothDevice device,
+                int prevState, int state) {
+            Logger.v(TAG, "onReceiveAudioStateChange (" + prevState +
+                    " -> " + state + ")");
+
+            invalidateOptionsMenu();
+        }
+
+        private void onReceiveActionConnectionStateChanged(BluetoothDevice device,
+                int prevState, int state, Bundle features) {
+            Logger.v(TAG, "onReceiveActionConnectionStateChanged: " +
+                    device.getAddress() + " (" +
+                    String.valueOf(prevState) + " -> " +
+                    String.valueOf(state) + ")");
+
+            switch (state) {
+                case BluetoothProfile.STATE_CONNECTED:
+                    updateAgFeatures(features);
+                    Toast.makeText(HfpTestActivity.this, R.string.msg_connection_success,
+                            Toast.LENGTH_SHORT).show();
+
+                    new MonkeyEvent("hfp-connected", true).send();
+
+                    break;
+                case BluetoothProfile.STATE_DISCONNECTED:
+                    new MonkeyEvent("hfp-disconnected", true).send();
+
+                    Toast.makeText(HfpTestActivity.this, R.string.msg_disconnection_success,
+                            Toast.LENGTH_SHORT).show();
+
+                    break;
+            }
+
+            invalidateOptionsMenu();
+        }
+    };
+
+    ///*
+     * HFP Service Connection.
+     ///
+    private final ServiceConnection mHfpServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            Logger.v(TAG, "onServiceConnected()");
+            mProfileService = ((ProfileService.LocalBinder) service).getService();
+            mBluetoothHandsfreeClient = mProfileService.getHfpClient();
+
+            int connState = mBluetoothHandsfreeClient.getConnectionState(mDevice);
+
+            if (connState == BluetoothProfile.STATE_CONNECTED) {
+                // trigger refresh of calls list
+                for (BluetoothHandsfreeClientCall call : mBluetoothHandsfreeClient
+                        .getCurrentCalls(mDevice)) {
+                    mCallsListFragment.onCallChanged(call);
+                }
+
+                // get supported AG features
+                updateAgFeatures(mBluetoothHandsfreeClient.getCurrentAgFeatures(mDevice));
+            }
+
+            // trigger refresh of indicators
+            mIndicatorsFragment.onAudioStateChanged(
+                    mBluetoothHandsfreeClient.getAudioState(mDevice), 0);
+            mIndicatorsFragment.onConnStateChanged(connState, 0);
+            mIndicatorsFragment.onAgEvent(mBluetoothHandsfreeClient.getCurrentAgEvents(mDevice));
+
+            invalidateOptionsMenu();
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            Logger.v(TAG, "onServiceDisconnected()");
+            mProfileService = null;
+            mBluetoothHandsfreeClient = null;
+
+            invalidateOptionsMenu();
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Logger.v(TAG, "onCreate()");
+
+        ActivityHelper.initialize(this, R.layout.activity_hfp_test);
+        BluetoothConnectionReceiver.registerObserver(this);
+
+        // bind to app service
+        Intent intent = new Intent(this, ProfileService.class);
+        bindService(intent, mHfpServiceConnection, BIND_AUTO_CREATE);
+
+        prepareActionBar();
+
+        mIndicatorsFragment = (IndicatorsFragment) getFragmentManager().findFragmentById(
+                R.id.indicators);
+        mCallsListFragment = (CallsListFragment) getFragmentManager().findFragmentById(
+                R.id.calls_list);
+        mDialpadFragment = (DialpadFragment) getFragmentManager().findFragmentById(R.id.dialpad);
+
+        setVolumeControlStream(AudioManager.STREAM_BLUETOOTH_SCO);
+    }
+
+    @Override
+    protected void onDestroy() {
+        Logger.v(TAG, "onDestroy");
+
+        super.onDestroy();
+
+        unbindService(mHfpServiceConnection);
+        BluetoothConnectionReceiver.removeObserver(this);
+    }
+
+    @Override
+    protected void onResume() {
+        Logger.v(TAG, "onResume");
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothHandsfreeClient.ACTION_CONNECTION_STATE_CHANGED);
+        filter.addAction(BluetoothHandsfreeClient.ACTION_AG_EVENT);
+        filter.addAction(BluetoothHandsfreeClient.ACTION_CALL_CHANGED);
+        filter.addAction(BluetoothHandsfreeClient.ACTION_AUDIO_STATE_CHANGED);
+        filter.addAction(BluetoothHandsfreeClient.ACTION_RESULT);
+        filter.addAction(BluetoothHandsfreeClient.ACTION_LAST_VTAG);
+        registerReceiver(mHfpClientReceiver, filter);
+        super.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        Logger.v(TAG, "onPause");
+
+        unregisterReceiver(mHfpClientReceiver);
+        super.onPause();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        mActionBarMenu = menu;
+
+        getMenuInflater().inflate(R.menu.menu_hfp_test, menu);
+        menu.findItem(R.id.menu_request_phone_num).setEnabled(mFeatVtag);
+
+        return true;
+    }
+
+    @Override
+    public void onDeviceChanged(BluetoothDevice device) {
+        Logger.v(TAG, "onDeviceChanged()");
+
+        mDevice = device;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.menu_request_phone_num:
+                mBluetoothHandsfreeClient.getLastVoiceTagNumber(mDevice);
+                return true;
+
+            case R.id.menu_call_history:
+                new CallHistoryDialogFragment(mCallHistory).show(getFragmentManager(),
+                        "call-history");
+                return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    public void onDeviceDisconected() {
+        Logger.v(TAG, "onDeviceDisconected");
+
+        invalidateOptionsMenu();
+    }
+
+    @Override
+    public void onCallHistoryDialogPositive(DialogFragment dialog, String number) {
+        mDialpadFragment.setNumber(number);
+    }
+
+    private void prepareActionBar() {
+        Logger.v(TAG, "prepareActionBar()");
+
+        mActionBar = getActionBar();
+        if (mActionBar != null) {
+            mActionBar.setTitle(R.string.title_hfp_test);
+            mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
+        }
+    }
+
+    private String resultCodeToString(int result, int cme) {
+        Logger.v(TAG, "resultCodeToString()");
+
+        switch (result) {
+            case BluetoothHandsfreeClient.ACTION_RESULT_OK:
+                return getString(R.string.hfptest_result_ok_text);
+            case BluetoothHandsfreeClient.ACTION_RESULT_ERROR:
+                return getString(R.string.hfptest_result_error_text);
+            case BluetoothHandsfreeClient.ACTION_RESULT_ERROR_NO_CARRIER:
+                return getString(R.string.hfptest_result_no_carrier_text);
+            case BluetoothHandsfreeClient.ACTION_RESULT_ERROR_BUSY:
+                return getString(R.string.hfptest_result_busy_text);
+            case BluetoothHandsfreeClient.ACTION_RESULT_ERROR_NO_ANSWER:
+                return getString(R.string.hfptest_result_no_answer_text);
+            case BluetoothHandsfreeClient.ACTION_RESULT_ERROR_DELAYED:
+                return getString(R.string.hfptest_result_delayed_text);
+            case BluetoothHandsfreeClient.ACTION_RESULT_ERROR_BLACKLISTED:
+                return getString(R.string.hfptest_result_blacklisted_text);
+            case BluetoothHandsfreeClient.ACTION_RESULT_ERROR_CME:
+                return getString(R.string.hfptest_result_cme_text) + " (" + cme + ")";
+        }
+
+        return getString(R.string.hfptest_call_state_unknown_text);
+    }
+
+    @Override
+    protected void onMonkeyQuery(String op, Bundle params) {
+        if ("getIndicators".equals(op)) {
+            Bundle bundle = mBluetoothHandsfreeClient.getCurrentAgEvents(mDevice);
+
+            MonkeyEvent me = new MonkeyEvent("hfp-ag-event", true);
+
+            if (bundle.containsKey(BluetoothHandsfreeClient.EXTRA_VOICE_RECOGNITION)) {
+                me.addReplyParam("vr", bundle.getInt(BluetoothHandsfreeClient.EXTRA_VOICE_RECOGNITION));
+            }
+
+            if (bundle.containsKey(BluetoothHandsfreeClient.EXTRA_IN_BAND_RING)) {
+                me.addReplyParam("in-band", bundle.getInt(BluetoothHandsfreeClient.EXTRA_IN_BAND_RING));
+            }
+
+            if (bundle.containsKey(BluetoothHandsfreeClient.EXTRA_NETWORK_STATUS)) {
+                me.addReplyParam("network-status",
+                        bundle.getInt(BluetoothHandsfreeClient.EXTRA_NETWORK_STATUS));
+            }
+
+            if (bundle.containsKey(BluetoothHandsfreeClient.EXTRA_NETWORK_ROAMING)) {
+                me.addReplyParam("network-roaming",
+                        bundle.getInt(BluetoothHandsfreeClient.EXTRA_NETWORK_ROAMING));
+            }
+
+            if (bundle.containsKey(BluetoothHandsfreeClient.EXTRA_NETWORK_SIGNAL_STRENGTH)) {
+                me.addReplyParam("signal-strength",
+                        bundle.getInt(BluetoothHandsfreeClient.EXTRA_NETWORK_SIGNAL_STRENGTH));
+            }
+
+            if (bundle.containsKey(BluetoothHandsfreeClient.EXTRA_BATTERY_LEVEL)) {
+                me.addReplyParam("battery-level",
+                        bundle.getInt(BluetoothHandsfreeClient.EXTRA_BATTERY_LEVEL));
+            }
+
+            if (bundle.containsKey(BluetoothHandsfreeClient.EXTRA_OPERATOR_NAME)) {
+                me.addReplyParam("operator-name",
+                        bundle.getString(BluetoothHandsfreeClient.EXTRA_OPERATOR_NAME));
+            }
+
+            if (bundle.containsKey(BluetoothHandsfreeClient.EXTRA_SUBSCRIBER_INFO)) {
+                me.addReplyParam("subscriber-info",
+                        bundle.getString(BluetoothHandsfreeClient.EXTRA_SUBSCRIBER_INFO));
+            }
+
+            me.send();
+        }
+    }
+
+    static String callToJson(BluetoothHandsfreeClientCall call) {
+        JSONObject json = new JSONObject();
+
+        try {
+            json.put("id", call.getId());
+            json.put("number", call.getNumber());
+            json.put("state", callStateToString(call.getState()));
+            json.put("outgoing", call.isOutgoing());
+            json.put("multiparty", call.isMultiParty());
+        } catch (JSONException e) {
+            // do nothing
+        }
+
+        return json.toString();
+    }
+
+    static String callStateToString(int state) {
+        switch (state) {
+            case BluetoothHandsfreeClientCall.CALL_STATE_ACTIVE:
+                return "active";
+            case BluetoothHandsfreeClientCall.CALL_STATE_HELD:
+                return "held";
+            case BluetoothHandsfreeClientCall.CALL_STATE_DIALING:
+                return "dialing";
+            case BluetoothHandsfreeClientCall.CALL_STATE_ALERTING:
+                return "alerting";
+            case BluetoothHandsfreeClientCall.CALL_STATE_INCOMING:
+                return "incoming";
+            case BluetoothHandsfreeClientCall.CALL_STATE_WAITING:
+                return "waiting";
+            case BluetoothHandsfreeClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD:
+                return "held_by_rnh";
+            case BluetoothHandsfreeClientCall.CALL_STATE_TERMINATED:
+                return "terminated";
+        }
+
+        return "unknown";
+    }
+
+    private void updateAgFeatures(Bundle b) {
+        // only supported AG features are being sent
+        mFeatVtag = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT);
+        mFeatVoiceRecognition = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION);
+        mFeatThreeWayCalling = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_3WAY_CALLING);
+        mFeatEnhancedCallControl = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_ECC);
+        mFeatReject = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_REJECT_CALL);
+        mFeatMerge = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_MERGE);
+        mFeatMergeDetach = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH);
+        mFeatReleaseAndAccept = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT);
+        mFeatAcceptHeldOrWaiting = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL);
+        mFeatReleaseHeldOrWaiting = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL);
+    }
+}*/
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/IBluetoothConnectionObserver.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/IBluetoothConnectionObserver.java
new file mode 100644
index 0000000..dd1df50
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/IBluetoothConnectionObserver.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.bluetooth.BluetoothDevice;
+
+public interface IBluetoothConnectionObserver {
+    public void onDeviceChanged(BluetoothDevice device);
+
+    public void onDeviceDisconected();
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/IndicatorsFragment.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/IndicatorsFragment.java
new file mode 100644
index 0000000..af451b0
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/IndicatorsFragment.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*package org.codeaurora.bttestapp;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.bluetooth.BluetoothHandsfreeClient;
+import android.bluetooth.BluetoothProfile;
+import android.os.Bundle;
+import android.text.SpannableString;
+import android.text.style.ForegroundColorSpan;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.CompoundButton;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+import com.qualcomm.bttestapp.R;
+*/
+/*public class IndicatorsFragment extends Fragment implements OnClickListener {
+
+    private HfpTestActivity mActivity;
+
+    private ToggleButton mIndConnState;
+
+    private ToggleButton mIndAudioState;
+
+    private ToggleButton mIndVrState;
+
+    private TextView mIndNetworkState;
+
+    private TextView mIndRoamingState;
+
+    private TextView mIndInbandState;
+
+    private TextView mIndSignalLevel;
+
+    private TextView mIndBatteryLevel;
+
+    private TextView mIndOperator;
+
+    private TextView mIndSubscriber;
+
+    private int mDefaultColor;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.indicators_fragment, null);
+
+        mIndConnState = (ToggleButton) view.findViewById(R.id.ind_conn_state);
+        mIndConnState.setOnClickListener(this);
+        mIndAudioState = (ToggleButton) view.findViewById(R.id.ind_audio_state);
+        mIndAudioState.setOnClickListener(this);
+        mIndVrState = (ToggleButton) view.findViewById(R.id.ind_vr_state);
+        mIndVrState.setOnClickListener(this);
+
+        mIndNetworkState = (TextView) view.findViewById(R.id.ind_network_state);
+        mIndRoamingState = (TextView) view.findViewById(R.id.ind_roaming_state);
+        mIndInbandState = (TextView) view.findViewById(R.id.ind_inband_state);
+
+        mIndSignalLevel = (TextView) view.findViewById(R.id.ind_signal_level);
+        mIndBatteryLevel = (TextView) view.findViewById(R.id.ind_battery_level);
+
+        mIndOperator = (TextView) view.findViewById(R.id.ind_operator);
+        mIndSubscriber = (TextView) view.findViewById(R.id.ind_subscriber);
+
+        mDefaultColor = mIndOperator.getTextColors().getDefaultColor();
+
+        resetIndicators(true);
+
+        return view;
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+
+        try {
+            mActivity = (HfpTestActivity) activity;
+        } catch (ClassCastException e) {
+            throw new ClassCastException(this.getClass().getSimpleName()
+                    + " can be only attached to " + HfpTestActivity.class.getSimpleName());
+        }
+    }
+
+    public void onConnStateChanged(int state, int prevState) {
+        switch (state) {
+            case BluetoothProfile.STATE_DISCONNECTED:
+                resetIndicators(false);
+                mIndConnState.setChecked(false);
+                mIndConnState.setEnabled(true);
+
+                mIndAudioState.setEnabled(false);
+                mIndVrState.setEnabled(false);
+                mIndVrState.setChecked(false);
+                break;
+
+            case BluetoothProfile.STATE_CONNECTING:
+                mIndConnState.setChecked(true);
+                mIndConnState.setEnabled(false);
+                break;
+
+            case BluetoothProfile.STATE_DISCONNECTING:
+                mIndConnState.setChecked(false);
+                mIndConnState.setEnabled(false);
+                break;
+
+            case BluetoothProfile.STATE_CONNECTED:
+                mIndConnState.setChecked(true);
+                mIndConnState.setEnabled(true);
+
+                mIndAudioState.setEnabled(true);
+                mIndVrState.setEnabled(mActivity.mFeatVoiceRecognition);
+
+                resetIndicators(false);
+                break;
+        }
+    }
+
+    public void onAudioStateChanged(int state, int prevState) {
+        switch (state) {
+            case BluetoothHandsfreeClient.STATE_AUDIO_DISCONNECTED:
+                mIndAudioState.setChecked(false);
+                mIndAudioState.setEnabled(true);
+                break;
+
+            case BluetoothHandsfreeClient.STATE_AUDIO_CONNECTING:
+                mIndAudioState.setChecked(true);
+                mIndAudioState.setEnabled(false);
+                break;
+
+            case BluetoothHandsfreeClient.STATE_AUDIO_CONNECTED:
+                mIndAudioState.setChecked(true);
+                mIndAudioState.setEnabled(true);
+                break;
+        }
+    }
+
+    public void onAgEvent(Bundle params) {
+        if (params == null) {
+            // it can happen when querying for indicators after connection and
+            // it's ok to ignore
+            return;
+        }
+
+        for (String param : params.keySet()) {
+
+            TextView colorInd = null;
+            // TextView valueInd = null;
+            TextView barInd = null;
+
+            if (param.equals(BluetoothHandsfreeClient.EXTRA_VOICE_RECOGNITION)) {
+
+                boolean enabled = (params.getInt(param) != 0);
+                mIndVrState.setChecked(enabled);
+                mIndVrState.setEnabled(mActivity.mFeatVoiceRecognition);
+
+            } else if (param.equals(BluetoothHandsfreeClient.EXTRA_IN_BAND_RING)) {
+
+                colorInd = mIndInbandState;
+
+            } else if (param.equals(BluetoothHandsfreeClient.EXTRA_OPERATOR_NAME)) {
+
+                setOperator(params.getString(param));
+
+            } else if (param.equals(BluetoothHandsfreeClient.EXTRA_NETWORK_STATUS)) {
+
+                colorInd = mIndNetworkState;
+
+            } else if (param.equals(BluetoothHandsfreeClient.EXTRA_NETWORK_ROAMING)) {
+
+                colorInd = mIndRoamingState;
+
+            } else if (param.equals(BluetoothHandsfreeClient.EXTRA_NETWORK_SIGNAL_STRENGTH)) {
+
+                barInd = mIndSignalLevel;
+
+            } else if (param.equals(BluetoothHandsfreeClient.EXTRA_BATTERY_LEVEL)) {
+
+                barInd = mIndBatteryLevel;
+
+            } else if (param.equals(BluetoothHandsfreeClient.EXTRA_SUBSCRIBER_INFO)) {
+
+                setSubscriber(params.getString(param));
+
+            }
+
+            if (colorInd != null) {
+                if (params.getInt(param, -1) != -1) {
+                    if (params.getInt(param) != 0) {
+                        colorInd.setBackgroundColor(getColor(R.color.ind_on));
+                    } else {
+                        colorInd.setBackgroundColor(getColor(R.color.ind_off));
+                    }
+                    colorInd.setVisibility(View.VISIBLE);
+                } else {
+                    // ignore
+                }
+            }
+
+            // replaced by barInd, but can be used in future
+            // if (valueInd != null) {
+            // if (params.getInt(param, -1) != -1) {
+            // valueInd.setText(Integer.toString(params.getInt(param)));
+            // } else {
+            // valueInd.setText("");
+            // }
+            // }
+
+            if (barInd != null) {
+                int val = params.getInt(param);
+                int color;
+
+                if (val < 2) {
+                    color = getColor(R.color.ind_bar_low);
+                } else if (val < 4) {
+                    color = getColor(R.color.ind_bar_medium);
+                } else {
+                    color = getColor(R.color.ind_bar_full);
+                }
+
+                SpannableString ss = new SpannableString(getResources().getString(R.string.ind_bar));
+                ss.setSpan(new ForegroundColorSpan(color), 0, val, 0);
+                ss.setSpan(new ForegroundColorSpan(getColor(R.color.ind_bar_off)), val,
+                        ss.length(), 0);
+
+                barInd.setText(ss);
+            }
+        }
+    }
+
+    private int getColor(int id) {
+        return getResources().getColor(id);
+    }
+
+    private void resetIndicators(boolean all) {
+        if (all) {
+            mIndConnState.setChecked(false);
+            mIndAudioState.setChecked(false);
+            mIndVrState.setChecked(false);
+
+            mIndConnState.setEnabled(true);
+            mIndAudioState.setEnabled(false);
+            mIndVrState.setEnabled(false);
+        }
+
+        mIndNetworkState.setBackgroundColor(getColor(R.color.ind_off));
+        mIndRoamingState.setBackgroundColor(getColor(R.color.ind_off));
+        mIndInbandState.setBackgroundColor(getColor(R.color.ind_off));
+
+        mIndSignalLevel.setText("");
+        mIndBatteryLevel.setText("");
+
+        setOperator(null);
+        setSubscriber(null);
+    }
+
+    private void setOperator(String text) {
+        if (text != null) {
+            mIndOperator.setText(text);
+            mIndOperator.setTextColor(mDefaultColor);
+        } else {
+            mIndOperator.setText(R.string.ind_operator_unknown);
+            mIndOperator.setTextColor(getColor(R.color.ind_text_unknown));
+        }
+    }
+
+    private void setSubscriber(String text) {
+        if (text != null) {
+            mIndSubscriber.setText(text);
+            mIndSubscriber.setTextColor(mDefaultColor);
+        } else {
+            mIndSubscriber.setText(R.string.ind_subscriber_unknown);
+            mIndSubscriber.setTextColor(getColor(R.color.ind_text_unknown));
+        }
+    }
+
+    @Override
+    public void onClick(View view) {
+        ///*
+         //* checked state of CompoundButton is toggled automatically when clicked
+         //* so since we need before-click state, it's always opposite of current
+         //* state
+         ///
+        boolean state = !((CompoundButton) view).isChecked();
+
+        switch (view.getId()) {
+            case R.id.ind_conn_state:
+                onClickConnState(state);
+                break;
+
+            case R.id.ind_audio_state:
+                onClickAudioState(state);
+                break;
+
+            case R.id.ind_vr_state:
+                onClickVrState(state);
+                break;
+        }
+    }
+
+    public void onClickConnState(boolean state) {
+        if (state) {
+            mActivity.mBluetoothHandsfreeClient.disconnect(mActivity.mDevice);
+        } else {
+            mActivity.mBluetoothHandsfreeClient.connect(mActivity.mDevice);
+        }
+    }
+
+    public void onClickAudioState(boolean state) {
+        if (state) {
+            mActivity.mBluetoothHandsfreeClient.disconnectAudio();
+        } else {
+            mActivity.mBluetoothHandsfreeClient.connectAudio();
+        }
+    }
+
+    public void onClickVrState(boolean state) {
+        if (state) {
+            mActivity.mBluetoothHandsfreeClient.stopVoiceRecognition(mActivity.mDevice);
+        } else {
+            mActivity.mBluetoothHandsfreeClient.startVoiceRecognition(mActivity.mDevice);
+        }
+        mIndVrState.setEnabled(false);
+    }
+}*/
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MainActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MainActivity.java
new file mode 100644
index 0000000..1a8c85f
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MainActivity.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothMasInstance;
+import android.bluetooth.BluetoothUuid;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcelable;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import org.codeaurora.bluetooth.bttestapp.R;
+import java.util.ArrayList;
+
+public class MainActivity extends MonkeyActivity {
+
+    private final String TAG = "MainActivity";
+
+    public static final String PREF_DEVICE = "device";
+    public static final String PREF_SERVICES = "services";
+
+    public static final String PICKER_ACTION = "android.bluetooth.devicepicker.action.LAUNCH";
+    public static final String PICKER_SELECTED = "android.bluetooth.devicepicker.action.DEVICE_SELECTED";
+
+    BluetoothDevice mDevice;
+
+    ProfileService mProfileService;
+
+    private boolean mIsBound = false;
+
+    private boolean mDiscoveryInProgress = false;
+
+    private ServicesFragment mServicesFragment = null;
+
+    private final BroadcastReceiver mPickerReceiver = new BroadcastReceiver() {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+
+            Log.v(TAG, "mPickerReceiver got " + action);
+
+            if (PICKER_SELECTED.equals(action)) {
+                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                updateDevice(device);
+                unregisterReceiver(this);
+
+                mServicesFragment.removeService(null);
+                mServicesFragment.persistServices();
+            }
+        }
+    };
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+
+            Log.v(TAG, "mReceiver got " + action);
+
+            if (BluetoothDevice.ACTION_UUID.equals(action)) {
+                BluetoothDevice dev = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                if (!dev.equals(mDevice)) {
+                    return;
+                }
+
+                Parcelable uuids[] = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
+
+                if (uuids != null) {
+                    for (Parcelable uuid : uuids) {
+                        if (BluetoothUuid.PBAP_PSE.equals(uuid)) {
+                            mServicesFragment.addService(ServicesFragment.Service.Type.PBAP, null);
+                        } /*else if (BluetoothUuid.Handsfree_AG.equals(uuid)) {
+                            mServicesFragment.addService(ServicesFragment.Service.Type.HFP, null);
+                        }*/
+                    }
+                }
+
+                mServicesFragment.persistServices();
+
+                if (mDiscoveryInProgress) {
+                    Log.v(TAG, "fetching MAS instances");
+                    mDevice.fetchMasInstances();
+                }
+
+            } else if (BluetoothDevice.ACTION_MAS_INSTANCE.equals(action)) {
+                BluetoothDevice dev = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                if (!dev.equals(mDevice)) {
+                    return;
+                }
+
+                ArrayList<BluetoothMasInstance> insts = intent
+                        .getParcelableArrayListExtra(BluetoothDevice.EXTRA_MAS_INSTANCE);
+
+                if (insts != null) {
+                    mProfileService.setMasInstances(insts);
+
+                    for (BluetoothMasInstance inst : insts) {
+                        mServicesFragment.addService(ServicesFragment.Service.Type.MAP, inst);
+                    }
+                }
+
+                mServicesFragment.persistServices();
+
+                mDiscoveryInProgress = false;
+            }
+        }
+    };
+
+    private final ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            mProfileService = ((ProfileService.LocalBinder) service).getService();
+            mProfileService.setDevice(mDevice);
+            mServicesFragment.restoreServices();
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName className) {
+            mProfileService = null;
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Log.v(TAG, "onCreate");
+
+        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
+        setContentView(R.layout.activity_main);
+
+        mServicesFragment = (ServicesFragment) getFragmentManager().findFragmentById(
+                R.id.services_list);
+
+        Intent intent = new Intent(this, ProfileService.class);
+        startService(intent);
+
+        if (bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
+            mIsBound = true;
+        }
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+
+        Log.v(TAG, "onStart");
+
+        String addr = getPreferences(MODE_PRIVATE).getString(PREF_DEVICE, null);
+
+        try {
+            BluetoothDevice dev = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(addr);
+            updateDevice(dev);
+        } catch (IllegalArgumentException e) {
+            // just leave device unset
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        Log.v(TAG, "onResume");
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothDevice.ACTION_UUID);
+        filter.addAction(BluetoothDevice.ACTION_MAS_INSTANCE);
+        registerReceiver(mReceiver, filter);
+
+        if (BluetoothAdapter.getDefaultAdapter().isEnabled() == false) {
+            Button b = (Button) findViewById(R.id.discover_services);
+            b.setEnabled(false);
+
+            b = (Button) findViewById(R.id.select_device);
+            b.setEnabled(false);
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+
+        Log.v(TAG, "onPause");
+
+        unregisterReceiver(mReceiver);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        Log.v(TAG, "onStop");
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+
+        if (mIsBound) {
+            unbindService(mConnection);
+        }
+
+        Log.v(TAG, "onDestroy");
+    }
+
+    public void onButtonClick(View v) {
+        if (v.getId() == R.id.select_device) {
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(PICKER_SELECTED);
+            registerReceiver(mPickerReceiver, filter);
+
+            Intent intent = new Intent(PICKER_ACTION);
+            intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            startActivity(intent);
+
+        } else if (v.getId() == R.id.discover_services) {
+            if (mDevice == null || mDiscoveryInProgress) {
+                return;
+            }
+
+            mServicesFragment.removeService(null);
+
+            Log.v(TAG, "fetching UUIDs");
+            mDiscoveryInProgress = mDevice.fetchUuidsWithSdp();
+        }
+    }
+
+    private void updateDevice(BluetoothDevice device) {
+        SharedPreferences.Editor prefs = getPreferences(MODE_PRIVATE).edit();
+
+        TextView name = (TextView) findViewById(R.id.device_name);
+        TextView addr = (TextView) findViewById(R.id.device_address);
+
+        if (device == null) {
+            name.setText(R.string.blank);
+            addr.setText(R.string.blank);
+
+            prefs.remove(PREF_DEVICE);
+        } else {
+            Intent intent = new Intent(BluetoothConnectionReceiver.ACTION_NEW_BLUETOOTH_DEVICE);
+            intent.putExtra(BluetoothConnectionReceiver.EXTRA_DEVICE_ADDRESS, device.getAddress());
+            sendBroadcast(intent);
+
+            name.setText(device.getName());
+            addr.setText(device.getAddress());
+
+            prefs.putString(PREF_DEVICE, device.getAddress());
+        }
+
+        prefs.commit();
+
+        mDevice = device;
+
+        if (mProfileService != null) {
+            mProfileService.setDevice(mDevice);
+        }
+    }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MapTestActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MapTestActivity.java
new file mode 100644
index 0000000..1aa5165
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MapTestActivity.java
@@ -0,0 +1,1446 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.ActionBar;
+import android.app.ActionBar.Tab;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentTransaction;
+import android.bluetooth.BluetoothMasInstance;
+import android.content.ComponentName;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.DatePicker;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.TimePicker;
+import android.widget.Toast;
+import android.widget.ViewFlipper;
+
+import com.android.vcard.VCardConstants;
+import com.android.vcard.VCardEntry;
+import com.android.vcard.VCardProperty;
+import org.codeaurora.bluetooth.bttestapp.R;
+import org.codeaurora.bluetooth.mapclient.BluetoothMapBmessage;
+import org.codeaurora.bluetooth.mapclient.BluetoothMapEventReport;
+import org.codeaurora.bluetooth.mapclient.BluetoothMapMessage;
+import org.codeaurora.bluetooth.mapclient.BluetoothMasClient;
+import org.codeaurora.bluetooth.mapclient.BluetoothMasClient.CharsetType;
+import org.codeaurora.bluetooth.mapclient.BluetoothMasClient.MessagesFilter;
+import org.codeaurora.bluetooth.bttestapp.GetTextDialogFragment.GetTextDialogListener;
+import org.codeaurora.bluetooth.bttestapp.R;
+import org.codeaurora.bluetooth.bttestapp.StringListDialogFragment.StringListDialogListener;
+import org.codeaurora.bluetooth.bttestapp.services.IMapServiceCallback;
+import org.codeaurora.bluetooth.bttestapp.util.Logger;
+import org.codeaurora.bluetooth.bttestapp.util.MonkeyEvent;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.List;
+
+public class MapTestActivity extends MonkeyActivity implements GetTextDialogListener,
+        StringListDialogListener {
+
+    private final String TAG = "MapTestActivity";
+
+    private final static short MAX_LIST_COUNT_DEFAULT = 0;
+    private final static short LIST_START_OFFSET_DEFAULT = 0;
+    private final static byte SUBJECT_LENGTH_DEFAULT = 0;
+    private final static String MESSAGES_FILTER_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
+
+    private static final int REQUEST_CODE_GET_PARAMETERS = 0;
+
+    private final String TAB_LIST = "List";
+    private final String TAB_PREVIEW = "Preview";
+    private final String TAB_EDIT = "Edit";
+
+    private int mMasInstanceId = -1;
+
+    private String mCurrentTab = TAB_LIST;
+
+    private final String[] mActionBarTabsNames = {
+            TAB_LIST, TAB_PREVIEW, TAB_EDIT
+    };
+
+    enum Job {
+        IDLE,
+        CONNECT,
+        DISCONNECT,
+        UPDATE_INBOX,
+        SET_PATH,
+        GET_MESSAGE_LISTING,
+        GET_MESSAGE_LISTING_SIZE,
+        GET_FOLDER_LISTING,
+        GET_FOLDER_LISTING_SIZE,
+        GET_MESSAGE,
+        SET_STATUS_READ,
+        SET_STATUS_UNREAD,
+        DELETE_MESSAGE,
+        PUSH_MESSAGE;
+    }
+
+    private Job mCurrentJob = Job.IDLE;
+
+    private String mStartingGetMessageHandle = null;
+
+    private String mPendingGetMessageHandle = null;
+
+    private ArrayDeque<String> mSetPathQueue = null;
+
+    private int mMessageListingParameters = 0;
+
+    private ActionBar mActionBar = null;
+
+    private ViewFlipper mViewFlipper = null;
+
+    private String mPreviewMsgHandle = null;
+    private BluetoothMapBmessage mMapBmessage = null;
+
+    // UI on List tab
+    private TextView mTextViewCurrentFolder = null;
+    private Spinner mSpinnerFolders = null;
+    private ArrayAdapter<String> mAdapterFolders = null;
+    private EditText mEditTextMaxListCountFolders = null;
+    private EditText mEditTextListStartOffsetFolders = null;
+    private EditText mEditTextMaxListCountMessages = null;
+    private EditText mEditTextListStartOffsetMessages = null;
+    private ListView mListViewMessages = null;
+    private EditText mEditTextSubjectLength = null;
+
+    private List<BluetoothMapMessage> mModelMessages = null;
+    private BluetoothMapMessageAdapter mAdapterMessages = null;
+
+    // UI on Preview/Edit tab
+    private EditText mViewBmsgHandle = null;
+    private EditText mViewBmsgStatus = null;
+    private EditText mViewBmsgType = null;
+    private EditText mViewBmsgFolder = null;
+    private EditText mViewBmsgEncoding = null;
+    private EditText mViewBmsgCharset = null;
+    private EditText mViewBmsgLanguage = null;
+    private EditText mViewBmsgContents = null;
+    private EditText mViewBmsgOrig = null;
+    private EditText mViewBmsgRcpt = null;
+
+    // MessagesFilter parameters
+    private byte mMessageType = MessagesFilter.MESSAGE_TYPE_ALL;
+    private Date mPeriodBegin = null;
+    private Date mPeriodEnd = null;
+    private byte mReadStatus = MessagesFilter.READ_STATUS_ANY;
+    private String mRecipient = null;
+    private String mOriginator = null;
+    private byte mPriority = MessagesFilter.PRIORITY_ANY;
+
+    private ArrayList<View> mListTouchables = null;
+
+    private final SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat(MESSAGES_FILTER_DATE_FORMAT);
+
+    private ProfileService mProfileService = null;
+
+    private final ArrayList<String> mEditOriginators = new ArrayList<String>();
+
+    private final ArrayList<String> mEditRecipients = new ArrayList<String>();
+
+    private final ServiceConnection mMapServiceConnection = new ServiceConnection() {
+
+        private void shortToast(String s) {
+            Toast.makeText(MapTestActivity.this, s, Toast.LENGTH_SHORT).show();
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mProfileService = ((ProfileService.LocalBinder) service).getService();
+
+            BluetoothMasInstance inst = mProfileService.getMapClient(mMasInstanceId)
+                    .getInstanceData();
+            MapTestActivity.this.getActionBar().setSubtitle(inst.getName());
+
+            mProfileService.setMapCallback(mMasInstanceId, new IMapServiceCallback() {
+
+                @Override
+                public void onConnect() {
+                    goToState(Job.IDLE);
+                    shortToast("MAS connect OK");
+                }
+
+                @Override
+                public void onConnectError() {
+                    goToState(Job.IDLE);
+                    shortToast("MAS connect FAILED");
+                }
+
+                @Override
+                public void onUpdateInbox() {
+                    goToState(Job.IDLE);
+                    new MonkeyEvent("map-updateinbox", true).send();
+                    shortToast("UpdateInbox OK");
+                }
+
+                @Override
+                public void onUpdateInboxError() {
+                    goToState(Job.IDLE);
+                    new MonkeyEvent("map-updateinbox", false).send();
+                    shortToast("UpdateInbox FAILED");
+                }
+
+                @Override
+                public void onSetPath(String path) {
+                    if (mSetPathQueue != null && mSetPathQueue.size() > 0) {
+                        String next = mSetPathQueue.removeFirst();
+                        mProfileService.getMapClient(mMasInstanceId).setFolderDown(next);
+                    } else {
+                        mTextViewCurrentFolder.setText(path);
+                        clearFolderList();
+
+                        goToState(Job.IDLE);
+                        new MonkeyEvent("map-setpath", true).addReplyParam("path", path)
+                                .send();
+                        shortToast("SetPath OK: path=" + path);
+                    }
+                }
+
+                @Override
+                public void onSetPathError(String path) {
+                    if (mSetPathQueue != null) {
+                        mSetPathQueue = null;
+                        mTextViewCurrentFolder.setText(path);
+                        clearFolderList();
+                    }
+
+                    goToState(Job.IDLE);
+                    new MonkeyEvent("map-setpath", false).addReplyParam("path", path)
+                            .send();
+                    shortToast("SetPath FAILED: path=" + path);
+                }
+
+                @Override
+                public void onGetMessagesListing(ArrayList<BluetoothMapMessage> messages) {
+                    mAdapterMessages.clear();
+                    mAdapterMessages.addAll(messages);
+
+                    updateListEmptyView(false);
+
+                    goToState(Job.IDLE);
+                    new MonkeyEvent("map-messageslisting", true)
+                            .addReplyParam("size", messages.size()).addExtReply(messages).send();
+                    shortToast("GetMessagesListing OK: size=" + messages.size());
+                }
+
+                @Override
+                public void onGetMessagesListingError() {
+                    updateListEmptyView(false);
+
+                    goToState(Job.IDLE);
+                    new MonkeyEvent("map-messageslisting", false).send();
+                    shortToast("GetMessagesListing FAILED");
+                }
+
+                @Override
+                public void onGetFolderListingSize(int size) {
+                    goToState(Job.IDLE);
+                    new MonkeyEvent("map-getfolderlisting-size", true)
+                            .addReplyParam("size", size)
+                            .send();
+                    shortToast("GetMessagesListing size OK: size=" + size);
+                }
+
+                @Override
+                public void onGetFolderListingSizeError() {
+                    goToState(Job.IDLE);
+                    new MonkeyEvent("map-getfolderlisting-size", false).send();
+                    shortToast("GetMessagesListing size FAILED");
+                }
+
+                @Override
+                public void onGetFolderListing(ArrayList<String> folders) {
+                    clearFolderList();
+                    mAdapterFolders.addAll(folders);
+
+                    goToState(Job.IDLE);
+                    new MonkeyEvent("map-getfolderlisting", true)
+                            .addReplyParam("size", folders.size()).addExtReply(folders).send();
+                    shortToast("GetFolderListing OK: size=" + folders.size());
+                }
+
+                @Override
+                public void onGetFolderListingError() {
+                    goToState(Job.IDLE);
+                    new MonkeyEvent("map-getfolderlisting", false).send();
+                    shortToast("GetFolderListing FAILED");
+                }
+
+                @Override
+                public void onGetMessage(BluetoothMapBmessage message) {
+                    updateMessage(message);
+
+                    goToState(Job.IDLE);
+                    new MonkeyEvent("map-getmessage", true)
+                            .addExtReply(message.toString()).send();
+                    shortToast("GetMessage OK");
+                }
+
+                @Override
+                public void onGetMessageError() {
+                    goToState(Job.IDLE);
+                    new MonkeyEvent("map-getmessage", false).send();
+                    shortToast("GetMessage FAILED");
+                }
+
+                @Override
+                public void onSetMessageStatus() {
+                    switch (mCurrentJob) {
+                        case SET_STATUS_READ:
+                            /* TODO: re-read message? check spec */
+                            break;
+                        case SET_STATUS_UNREAD:
+                            /* TODO: re-read message? check spec */
+                            break;
+                        case DELETE_MESSAGE:
+                            resetPreviewEditUi();
+                            Toast.makeText(MapTestActivity.this, "Message deleted!",
+                                    Toast.LENGTH_SHORT).show();
+                            break;
+                        default:
+                            break;
+                    }
+
+                    goToState(Job.IDLE);
+                    new MonkeyEvent("map-setmessagestatus", true).send();
+                    shortToast("SetMessageStatus OK");
+                }
+
+                @Override
+                public void onSetMessageStatusError() {
+                    goToState(Job.IDLE);
+                    new MonkeyEvent("map-setmessagestatus", false).send();
+                    shortToast("SetMessageStatus FAILED");
+                }
+
+                @Override
+                public void onPushMessage(String handle) {
+                    goToState(Job.IDLE);
+                    new MonkeyEvent("map-pushmessage", true).addReplyParam("handle", handle)
+                            .send();
+                    shortToast("PushMessage OK: handle=" + handle);
+                }
+
+                @Override
+                public void onPushMessageError() {
+                    goToState(Job.IDLE);
+                    new MonkeyEvent("map-pushmessage", false).send();
+                    shortToast("PushMessage FAILED");
+                }
+
+                @Override
+                public void onGetMessagesListingSize(int size) {
+                    goToState(Job.IDLE);
+                    new MonkeyEvent("map-getmessageslisting-size", true)
+                            .addReplyParam("size", size).send();
+                    shortToast("GetMessagesListing size OK: size=" + size);
+                }
+
+                @Override
+                public void onGetMessagesListingSizeError() {
+                    goToState(Job.IDLE);
+                    new MonkeyEvent("map-getmessageslisting-size", false).send();
+                    shortToast("GetMessagesListing size FAILED");
+                }
+
+                @Override
+                public void onEventReport(BluetoothMapEventReport eventReport) {
+                    String msgType = (eventReport.getMsgType() != null) ?
+                            eventReport.getMsgType().toString() : getString(R.string.blank);
+
+                    new MonkeyEvent("map-eventreport", true)
+                            .addReplyParam("type", eventReport.getType().toString())
+                            .addReplyParam("handle", eventReport.getHandle())
+                            .addReplyParam("folder", eventReport.getFolder())
+                            .addReplyParam("old_folder", eventReport.getOldFolder())
+                            .addReplyParam("msg_type", msgType)
+                            .send();
+                }
+            });
+
+            ProfileService.MapSessionData map = mProfileService.getMapSessionData(mMasInstanceId);
+            if (map != null) {
+                if (map.getFolderListing != null) {
+                    clearFolderList();
+                    mAdapterFolders.addAll(map.getFolderListing);
+                }
+
+                if (map.getMessagesListing != null) {
+                    mAdapterMessages.clear();
+                    mAdapterMessages.addAll(map.getMessagesListing);
+                    updateListEmptyView(false);
+                }
+
+                if (map.getMessage != null) {
+                    updateMessage(map.getMessage);
+                }
+            }
+
+            mTextViewCurrentFolder.setText(mProfileService.getMapClient(mMasInstanceId)
+                    .getCurrentPath());
+
+            updateUi(true);
+
+            if (mStartingGetMessageHandle != null) {
+                getMessage(mStartingGetMessageHandle, CharsetType.UTF_8, false);
+                mStartingGetMessageHandle = null;
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            mProfileService = null;
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent intent = getIntent();
+
+        ActivityHelper.initialize(this, R.layout.activity_map_test);
+        ActivityHelper.setActionBarTitle(this, R.string.title_map_test);
+
+        mMasInstanceId = intent.getIntExtra(ProfileService.EXTRA_MAP_INSTANCE_ID, -1);
+
+        if (ProfileService.ACTION_MAP_GET_MESSAGE.equals(intent.getAction())) {
+            mStartingGetMessageHandle = intent
+                    .getStringExtra(ProfileService.EXTRA_MAP_MESSAGE_HANDLE);
+        }
+
+        if (mMasInstanceId < 0) {
+            Log.e(TAG, "Cannot start MAP activity without instance information");
+            finish();
+        }
+
+        intent = new Intent(MapTestActivity.this, ProfileService.class);
+        bindService(intent, mMapServiceConnection, BIND_AUTO_CREATE);
+
+        mViewFlipper = (ViewFlipper) findViewById(R.id.maptest_viewflipper);
+        mTextViewCurrentFolder = (TextView) findViewById(R.id.maptest_nav_current);
+        mSpinnerFolders = (Spinner) findViewById(R.id.maptest_nav_folders);
+        mAdapterFolders = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
+        mSpinnerFolders.setAdapter(mAdapterFolders);
+        mEditTextMaxListCountFolders = (EditText) findViewById(R.id.maptest_nav_list_max);
+        mEditTextMaxListCountFolders.setText(String.valueOf(MAX_LIST_COUNT_DEFAULT));
+        mEditTextListStartOffsetFolders = (EditText) findViewById(R.id.maptest_nav_list_offset);
+        mEditTextListStartOffsetFolders.setText(String.valueOf(LIST_START_OFFSET_DEFAULT));
+        mEditTextMaxListCountMessages = (EditText) findViewById(R.id.maptest_msglist_max);
+        mEditTextMaxListCountMessages.setText(String.valueOf(MAX_LIST_COUNT_DEFAULT));
+        mEditTextListStartOffsetMessages = (EditText) findViewById(R.id.maptest_msglist_offset);
+        mEditTextListStartOffsetMessages.setText(String.valueOf(LIST_START_OFFSET_DEFAULT));
+        mListViewMessages = (ListView) findViewById(R.id.maptest_msglist_lv);
+        mListViewMessages.setEmptyView(findViewById(R.id.maptest_msglist_empty));
+        mModelMessages = new ArrayList<BluetoothMapMessage>();
+        mAdapterMessages = new BluetoothMapMessageAdapter();
+        mListViewMessages.setAdapter(mAdapterMessages);
+        mListViewMessages.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                String handle = mModelMessages.get(position).getHandle();
+
+                getMessage(handle, CharsetType.UTF_8, false);
+            }
+        });
+        registerForContextMenu(mListViewMessages);
+
+        mEditTextSubjectLength = (EditText) findViewById(R.id.maptest_msglist_subject_len);
+        mEditTextSubjectLength.setText(String.valueOf(SUBJECT_LENGTH_DEFAULT));
+        mViewBmsgHandle = (EditText) findViewById(R.id.map_msg_handle);
+        mViewBmsgStatus = (EditText) findViewById(R.id.maptest_bmsg_status);
+        mViewBmsgType = (EditText) findViewById(R.id.maptest_bmsg_type);
+        mViewBmsgFolder = (EditText) findViewById(R.id.maptest_bmsg_folder);
+        mViewBmsgEncoding = (EditText) findViewById(R.id.maptest_bbody_encoding);
+        mViewBmsgCharset = (EditText) findViewById(R.id.maptest_bbody_charset);
+        mViewBmsgLanguage = (EditText) findViewById(R.id.maptest_bbody_language);
+        mViewBmsgContents = (EditText) findViewById(R.id.maptest_message);
+        mViewBmsgOrig = (EditText) findViewById(R.id.maptest_orig);
+        mViewBmsgRcpt = (EditText) findViewById(R.id.maptest_rcpt);
+
+        clearFolderList();
+        updateListEmptyView(false);
+
+        mActionBar = getActionBar();
+        mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+
+        ActionBar.TabListener tabListener = new ActionBar.TabListener() {
+            @Override
+            public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+            }
+
+            @Override
+            public void onTabSelected(Tab tab, FragmentTransaction ft) {
+                mCurrentTab = tab.getText().toString();
+
+                if (mCurrentTab.equals(TAB_LIST)) {
+                    mViewFlipper.setDisplayedChild(0);
+                } else if (mCurrentTab.equals(TAB_PREVIEW)) {
+                    mViewFlipper.setDisplayedChild(1);
+                } else if (mCurrentTab.equals(TAB_EDIT)) {
+                    mViewFlipper.setDisplayedChild(2);
+                }
+
+                invalidateOptionsMenu();
+            }
+
+            @Override
+            public void onTabReselected(Tab tab, FragmentTransaction ft) {
+            }
+        };
+
+        for (String tab : mActionBarTabsNames) {
+            mActionBar.addTab(
+                    mActionBar.newTab()
+                            .setText(tab)
+                            .setTabListener(tabListener));
+
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+
+        if (requestCode == REQUEST_CODE_GET_PARAMETERS) {
+            if (resultCode == RESULT_OK) {
+                mMessageListingParameters = (int) data.getLongExtra("result", 0);
+            }
+        }
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        Logger.v(TAG, "onStart()");
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        Logger.v(TAG, "onStop()");
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        Logger.v(TAG, "onDestroy()");
+        if (mProfileService != null) {
+            mProfileService.setMapCallback(mMasInstanceId, null);
+        }
+        unbindService(mMapServiceConnection);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        mActionBarMenu = menu;
+
+        getMenuInflater().inflate(R.menu.menu_map_test, menu);
+
+        boolean connected = false;
+
+        if (mProfileService != null) {
+            connected = (mProfileService.getMapClient(mMasInstanceId).getState() == BluetoothMasClient.ConnectionState.CONNECTED);
+        }
+
+        menu.findItem(R.id.menu_map_connect).setVisible(!connected);
+        menu.findItem(R.id.menu_map_disconnect).setVisible(connected);
+        menu.findItem(R.id.menu_map_update_inbox).setVisible(connected);
+
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.menu_map_connect:
+                onClickConnect();
+                break;
+            case R.id.menu_map_disconnect:
+                onClickDisconnect();
+                break;
+            case R.id.menu_map_update_inbox:
+                onClickUpdateInbox();
+                break;
+            case R.id.menu_map_goto_inbox:
+                goToFolder("telecom/msg/inbox");
+                break;
+            case R.id.menu_map_goto_outbox:
+                goToFolder("telecom/msg/outbox");
+                break;
+            case R.id.menu_map_goto_draft:
+                goToFolder("telecom/msg/draft");
+                break;
+            default:
+                Logger.w(TAG, "Unknown item selected.");
+                break;
+        }
+
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void clearFolderList() {
+        mAdapterFolders.clear();
+        mAdapterFolders.add(".");
+    }
+
+    private void onClickConnect() {
+        mProfileService.getMapClient(mMasInstanceId).connect();
+        goToState(Job.CONNECT);
+    }
+
+    private void onClickDisconnect() {
+        mProfileService.getMapClient(mMasInstanceId).disconnect();
+        goToState(Job.DISCONNECT);
+    }
+
+    private void onClickUpdateInbox() {
+        mProfileService.getMapClient(mMasInstanceId).updateInbox();
+        goToState(Job.UPDATE_INBOX);
+    }
+
+    private VCardEntry createVcard(BluetoothMapBmessage.Type type, String val) {
+        VCardEntry vcard = new VCardEntry();
+        VCardProperty prop = new VCardProperty();
+
+        prop.setName(VCardConstants.PROPERTY_N);
+        prop.setValues(val);
+        vcard.addProperty(prop);
+
+        if (BluetoothMapBmessage.Type.EMAIL.equals(type)) {
+            prop.setName(VCardConstants.PROPERTY_EMAIL);
+        } else {
+            prop.setName(VCardConstants.PROPERTY_TEL);
+        }
+        vcard.addProperty(prop);
+
+        return vcard;
+    }
+
+    public void onClickPushMessage(View v) {
+        boolean transparent = ((CheckBox) findViewById(R.id.map_msg_push_transparent)).isChecked();
+        boolean retry = ((CheckBox) findViewById(R.id.map_msg_push_retry)).isChecked();
+        Spinner typeView = (Spinner) findViewById(R.id.bmsgedit_type);
+        Spinner encView = (Spinner) findViewById(R.id.bmsgedit_encoding);
+        EditText contentsView = (EditText) findViewById(R.id.bmsgedit_contents);
+        int charset = ((RadioGroup) findViewById(R.id.map_msg_push_charset))
+                .getCheckedRadioButtonId();
+
+        BluetoothMapBmessage bmsg = new BluetoothMapBmessage();
+
+        bmsg.setStatus(BluetoothMapBmessage.Status.UNREAD);
+
+        for (BluetoothMapBmessage.Type t : BluetoothMapBmessage.Type.values()) {
+            if (t.name().equals(typeView.getSelectedItem().toString())) {
+                bmsg.setType(t);
+                break;
+            }
+        }
+
+        if (charset != R.id.map_msg_push_charset_native) {
+            bmsg.setCharset("UTF-8");
+        }
+
+        if (encView.getSelectedItemPosition() > 0) {
+            bmsg.setEncoding(encView.getSelectedItem().toString());
+        }
+
+        bmsg.setFolder(mProfileService.getMapClient(mMasInstanceId).getCurrentPath());
+
+        for (String rcpt : mEditOriginators) {
+            bmsg.addRecipient(createVcard(bmsg.getType(), rcpt));
+        }
+
+        for (String orig : mEditRecipients) {
+            bmsg.addRecipient(createVcard(bmsg.getType(), orig));
+        }
+
+        bmsg.setBodyContent(contentsView.getText().toString());
+
+        mProfileService
+                .getMapClient(mMasInstanceId)
+                .pushMessage(
+                        null,
+                        bmsg,
+                        charset == R.id.map_msg_push_charset_native ? BluetoothMasClient.CharsetType.NATIVE
+                                : BluetoothMasClient.CharsetType.UTF_8, transparent, retry);
+
+        goToState(Job.PUSH_MESSAGE);
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        int instanceId = intent.getIntExtra(ProfileService.EXTRA_MAP_INSTANCE_ID, -1);
+
+        if (instanceId < 0) {
+            // don't care if instance info is missing
+            return;
+        }
+
+        // in case request is for different MAS instance than activity is
+        // running, we'll just restart activity using the same intent to get
+        // proper MAS instance
+        if (instanceId != mMasInstanceId) {
+            finish();
+            overridePendingTransition(0, 0);
+            startActivity(intent);
+            overridePendingTransition(0, 0);
+            return;
+        }
+
+        if (ProfileService.ACTION_MAP_GET_MESSAGE.equals(intent.getAction())) {
+            String handle = intent.getStringExtra(ProfileService.EXTRA_MAP_MESSAGE_HANDLE);
+            getMessage(handle, CharsetType.UTF_8, false);
+        }
+    }
+
+    public void onClickDeleteMessage(View view) {
+        if (mPreviewMsgHandle != null) {
+            mProfileService.getMapClient(mMasInstanceId).setMessageDeletedStatus(mPreviewMsgHandle,
+                    true);
+            goToState(Job.DELETE_MESSAGE);
+        }
+    }
+
+    public void onClickSetStatus(View view) {
+        if (mMapBmessage == null || mPreviewMsgHandle == null) {
+            return;
+        }
+
+        if (mMapBmessage.getStatus().equals(BluetoothMapBmessage.Status.READ)) {
+            mProfileService.getMapClient(mMasInstanceId).setMessageReadStatus(mPreviewMsgHandle,
+                    false);
+            goToState(Job.SET_STATUS_UNREAD);
+        } else {
+            mProfileService.getMapClient(mMasInstanceId).setMessageReadStatus(mPreviewMsgHandle,
+                    true);
+            goToState(Job.SET_STATUS_READ);
+        }
+    }
+
+    public void onClickSetPathRoot(View view) {
+        mProfileService.getMapClient(mMasInstanceId).setFolderRoot();
+        goToState(Job.SET_PATH);
+    }
+
+    public void onClickSetPathUp(View view) {
+        mProfileService.getMapClient(mMasInstanceId).setFolderUp();
+        goToState(Job.SET_PATH);
+    }
+
+    public void onClickSetPathEnter(View view) {
+        String folder = mSpinnerFolders.getSelectedItem().toString();
+
+        // do not let go to current folder
+        if (folder.equals(".")) {
+            return;
+        }
+
+        mProfileService.getMapClient(mMasInstanceId).setFolderDown(folder);
+        goToState(Job.SET_PATH);
+    }
+
+    public void onClickGetFolderListing(View view) {
+        int count = Integer.parseInt(mEditTextMaxListCountFolders.getText().toString());
+        int offset = Integer.parseInt(mEditTextListStartOffsetFolders.getText().toString());
+
+        try {
+            mProfileService.getMapClient(mMasInstanceId).getFolderListing(count, offset);
+            goToState(Job.GET_FOLDER_LISTING);
+        } catch (IllegalArgumentException e) {
+            Toast.makeText(this,
+                    "GetFolderListing FAILED: illegal arguments (" + e.getMessage() + ")",
+                    Toast.LENGTH_LONG).show();
+        }
+    }
+
+    public void onClickGetFolderListingSize(View view) {
+        mProfileService.getMapClient(mMasInstanceId).getFolderListingSize();
+        goToState(Job.GET_FOLDER_LISTING_SIZE);
+    }
+
+    public void onClickMessageParameters(View view) {
+        Intent intent = new Intent(MapTestActivity.this, MessageFilterActivity.class);
+        intent.putExtra("filter", (long) mMessageListingParameters);
+        startActivityForResult(intent, REQUEST_CODE_GET_PARAMETERS);
+    }
+
+    public MessagesFilter getLocalMessageFilter() {
+        MessagesFilter messageFilter = new MessagesFilter();
+        messageFilter.setMessageType(mMessageType);
+        messageFilter.setOriginator(mOriginator);
+        messageFilter.setPeriod(mPeriodBegin, mPeriodEnd);
+        messageFilter.setPriority(mPriority);
+        messageFilter.setReadStatus(mReadStatus);
+        messageFilter.setRecipient(mRecipient);
+        return messageFilter;
+    }
+
+    public class MessageFilterDialogFragment extends DialogFragment {
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            View dialogView = MapTestActivity.this.getLayoutInflater().inflate(
+                    R.layout.messages_filter, null);
+
+            final MessageFilterHolder holder = new MessageFilterHolder(dialogView);
+            holder.populate();
+
+            final AlertDialog.Builder alertBuilder = new AlertDialog.Builder(MapTestActivity.this);
+            alertBuilder.setView(dialogView);
+            alertBuilder
+                    .setTitle(getResources().getString(R.string.dialog_title_create_msg_filter));
+            alertBuilder.setPositiveButton(android.R.string.ok,
+                    new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            holder.save();
+                        }
+                    });
+            alertBuilder.setNegativeButton(android.R.string.cancel, null);
+            alertBuilder.setNeutralButton(getResources().getString(R.string.clear), null);
+
+            // After click on button with assigned listener in function
+            // setNeutralButton, the dialog automatically will close. To avoid
+            // it,
+            // onClickListener for this button have to be assigned manually.
+            final AlertDialog alertDialog = alertBuilder.create();
+            alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
+                @Override
+                public void onShow(DialogInterface dialog) {
+                    Button clearButton = alertDialog.getButton(DialogInterface.BUTTON_NEUTRAL);
+                    clearButton.setOnClickListener(new View.OnClickListener() {
+                        @Override
+                        public void onClick(View v) {
+                            holder.clear();
+                        }
+                    });
+                }
+            });
+
+            return alertDialog;
+        }
+    }
+
+    public void onClickGetMessagesFilter(View view) {
+        new MessageFilterDialogFragment().show(getFragmentManager(), "msg_filter");
+    }
+
+    class MessageFilterHolder {
+        CheckBox type_sms_gsm = null;
+        CheckBox type_sms_cdma = null;
+        CheckBox type_email = null;
+        CheckBox type_mms = null;
+        RadioButton status_read_all = null;
+        RadioButton status_unread = null;
+        RadioButton status_read = null;
+        RadioButton priority_all = null;
+        RadioButton priority_high = null;
+        RadioButton priority_non_high = null;
+        EditText period_begin = null;
+        EditText period_end = null;
+        EditText recipient = null;
+        EditText originator = null;
+        ImageButton pickPeriodBegin = null;
+        ImageButton pickPeriodEnd = null;
+
+        MessageFilterHolder(View row) {
+            type_sms_gsm = (CheckBox) row.findViewById(R.id.map_filter_type_sms_gsm);
+            type_sms_cdma = (CheckBox) row.findViewById(R.id.map_filter_type_sms_cdma);
+            type_email = (CheckBox) row.findViewById(R.id.map_filter_type_email);
+            type_mms = (CheckBox) row.findViewById(R.id.map_filter_type_mms);
+            status_read_all = (RadioButton) row.findViewById(R.id.map_filter_read_status_all);
+            status_unread = (RadioButton) row.findViewById(R.id.map_filter_read_status_unread);
+            status_read = (RadioButton) row.findViewById(R.id.map_filter_read_status_read);
+            priority_all = (RadioButton) row.findViewById(R.id.map_filter_priority_all);
+            priority_high = (RadioButton) row.findViewById(R.id.map_filter_priority_high);
+            priority_non_high = (RadioButton) row.findViewById(R.id.map_filter_status_non_high);
+            period_begin = (EditText) row.findViewById(R.id.map_filter_period_begin);
+            period_end = (EditText) row.findViewById(R.id.map_filter_period_end);
+            recipient = (EditText) row.findViewById(R.id.map_filter_recipient);
+            originator = (EditText) row.findViewById(R.id.map_filter_originator);
+            pickPeriodBegin = (ImageButton) row.findViewById(R.id.map_pick_data_period_begin);
+            pickPeriodBegin.setOnClickListener(new View.OnClickListener() {
+
+                @Override
+                public void onClick(View v) {
+                    new DateTimePicker(period_begin).pick();
+                }
+            });
+            pickPeriodEnd = (ImageButton) row.findViewById(R.id.map_pick_data_period_end);
+            pickPeriodEnd.setOnClickListener(new View.OnClickListener() {
+
+                @Override
+                public void onClick(View v) {
+                    new DateTimePicker(period_end).pick();
+                }
+            });
+        }
+
+        void populate() {
+            type_sms_gsm.setChecked((mMessageType & MessagesFilter.MESSAGE_TYPE_SMS_GSM) != 0);
+            type_sms_cdma.setChecked((mMessageType & MessagesFilter.MESSAGE_TYPE_SMS_CDMA) != 0);
+            type_email.setChecked((mMessageType & MessagesFilter.MESSAGE_TYPE_EMAIL) != 0);
+            type_mms.setChecked((mMessageType & MessagesFilter.MESSAGE_TYPE_MMS) != 0);
+            status_read_all.setChecked(mReadStatus == MessagesFilter.READ_STATUS_ANY);
+            status_read.setChecked(mReadStatus == MessagesFilter.READ_STATUS_READ);
+            status_unread.setChecked(mReadStatus == MessagesFilter.READ_STATUS_UNREAD);
+            priority_all.setChecked(mPriority == MessagesFilter.PRIORITY_ANY);
+            priority_high.setChecked(mPriority == MessagesFilter.PRIORITY_HIGH);
+            priority_non_high.setChecked(mPriority == MessagesFilter.PRIORITY_NON_HIGH);
+
+            if (mPeriodBegin != null) {
+                period_begin.setText(mSimpleDateFormat.format(mPeriodBegin));
+            } else {
+                period_begin.setText(null);
+            }
+
+            if (mPeriodEnd != null) {
+                period_end.setText(mSimpleDateFormat.format(mPeriodEnd));
+            } else {
+                period_end.setText(null);
+            }
+
+            recipient.setText(mRecipient);
+            originator.setText(mOriginator);
+        }
+
+        void clear() {
+            mMessageType = MessagesFilter.MESSAGE_TYPE_ALL;
+            mPeriodBegin = null;
+            mPeriodEnd = null;
+            mReadStatus = MessagesFilter.READ_STATUS_ANY;
+            mRecipient = null;
+            mOriginator = null;
+            mPriority = MessagesFilter.PRIORITY_ANY;
+
+            populate();
+        }
+
+        void save() {
+            mMessageType = MessagesFilter.MESSAGE_TYPE_ALL;
+
+            if (type_sms_gsm.isChecked()) {
+                mMessageType |= MessagesFilter.MESSAGE_TYPE_SMS_GSM;
+            }
+
+            if (type_sms_cdma.isChecked()) {
+                mMessageType |= MessagesFilter.MESSAGE_TYPE_SMS_CDMA;
+            }
+
+            if (type_email.isChecked()) {
+                mMessageType |= MessagesFilter.MESSAGE_TYPE_EMAIL;
+            }
+
+            if (type_mms.isChecked()) {
+                mMessageType |= MessagesFilter.MESSAGE_TYPE_MMS;
+            }
+
+            if (status_read.isChecked()) {
+                mReadStatus = MessagesFilter.READ_STATUS_READ;
+            } else if (status_unread.isChecked()) {
+                mReadStatus = MessagesFilter.READ_STATUS_UNREAD;
+            } else {
+                mReadStatus = MessagesFilter.READ_STATUS_ANY;
+            }
+
+            if (priority_high.isChecked()) {
+                mPriority = MessagesFilter.PRIORITY_HIGH;
+            } else if (priority_non_high.isChecked()) {
+                mPriority = MessagesFilter.PRIORITY_NON_HIGH;
+            } else {
+                mPriority = MessagesFilter.PRIORITY_ANY;
+            }
+
+            try {
+                mPeriodBegin = mSimpleDateFormat.parse(period_begin.getText().toString());
+            } catch (ParseException e) {
+                mPeriodBegin = null;
+                Logger.e(TAG, "Exception during parse begin period!");
+            }
+
+            try {
+                mPeriodEnd = mSimpleDateFormat.parse(period_end.getText().toString());
+            } catch (ParseException e) {
+                mPeriodEnd = null;
+                Logger.e(TAG, "Exception during parse end period!");
+            }
+
+            mRecipient = recipient.getText().toString();
+            mOriginator = originator.getText().toString();
+        }
+    }
+
+    public void onClickGetMessagesListing(View view) {
+        String folder = mSpinnerFolders.getSelectedItem().toString();
+        int maxListCount = Integer.parseInt(
+                mEditTextMaxListCountMessages.getText().toString());
+        int listStartOffset = Integer.parseInt(
+                mEditTextListStartOffsetMessages.getText().toString());
+        int subjectLength = Integer.parseInt(
+                mEditTextSubjectLength.getText().toString());
+
+        if (folder.equals(".")) {
+            folder = "";
+        }
+
+        try {
+            mProfileService.getMapClient(mMasInstanceId).getMessagesListing(folder,
+                    mMessageListingParameters,
+                    getLocalMessageFilter(), subjectLength, maxListCount, listStartOffset);
+
+            goToState(Job.GET_MESSAGE_LISTING);
+            updateListEmptyView(true);
+        } catch (IllegalArgumentException e) {
+            Toast.makeText(this,
+                    "GetMessagesListing FAILED: illegal arguments (" + e.getMessage() + ")",
+                    Toast.LENGTH_LONG).show();
+        }
+    }
+
+    public void updateListEmptyView(boolean working) {
+        View progressBar = findViewById(R.id.maptest_msglist_progressbar);
+        View textView = findViewById(R.id.maptest_msglist_empty);
+
+        if (working) {
+            textView.setVisibility(View.GONE);
+            mListViewMessages.setEmptyView(progressBar);
+        } else {
+            progressBar.setVisibility(View.GONE);
+            mListViewMessages.setEmptyView(textView);
+        }
+    }
+
+    public void onClickGetMessageListingSize(View view) {
+        mProfileService.getMapClient(mMasInstanceId).getMessagesListingSize();
+        goToState(Job.GET_MESSAGE_LISTING_SIZE);
+    }
+
+    class BluetoothMapMessageAdapter extends ArrayAdapter<BluetoothMapMessage> {
+        BluetoothMapMessageAdapter() {
+            super(MapTestActivity.this, android.R.layout.simple_list_item_1, mModelMessages);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            View v = convertView;
+
+            if (v == null) {
+                v = getLayoutInflater().inflate(R.layout.message_row, parent, false);
+            }
+
+            BluetoothMapMessage msg = mModelMessages.get(position);
+
+            ((TextView) v.findViewById(R.id.message_row_type)).setText(msg.getType().toString());
+            ((TextView) v.findViewById(R.id.message_row_handle)).setText(msg.getHandle());
+
+            ((TextView) v.findViewById(R.id.message_row_flag_text))
+                    .setTextColor(msg.isText() ? Color.YELLOW : Color.DKGRAY);
+            ((TextView) v.findViewById(R.id.message_row_flag_read))
+                    .setTextColor(msg.isRead() ? Color.YELLOW : Color.DKGRAY);
+            ((TextView) v.findViewById(R.id.message_row_flag_sent))
+                    .setTextColor(msg.isSent() ? Color.YELLOW : Color.DKGRAY);
+            ((TextView) v.findViewById(R.id.message_row_flag_drm))
+                    .setTextColor(msg.isProtected() ? Color.YELLOW : Color.DKGRAY);
+            ((TextView) v.findViewById(R.id.message_row_flag_prio))
+                    .setTextColor(msg.isPriority() ? Color.YELLOW : Color.DKGRAY);
+
+            ((TextView) v.findViewById(R.id.message_row_subject)).setText(msg.getSubject());
+
+            ((TextView) v.findViewById(R.id.message_row_date))
+                    .setText(new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(msg.getDateTime()));
+
+            ((TextView) v.findViewById(R.id.message_row_from)).setText(msg.getSenderAddressing());
+            if (msg.getSenderAddressing() != null && !msg.getSenderAddressing().isEmpty()) {
+                ((TextView) v.findViewById(R.id.message_row_from_lbl))
+                        .setVisibility(View.VISIBLE);
+            } else {
+                ((TextView) v.findViewById(R.id.message_row_from_lbl))
+                        .setVisibility(View.INVISIBLE);
+            }
+
+            ((TextView) v.findViewById(R.id.message_row_to)).setText(msg.getRecipientAddressing());
+            if (msg.getRecipientAddressing() != null && !msg.getRecipientAddressing().isEmpty()) {
+                ((TextView) v.findViewById(R.id.message_row_to_lbl)).setVisibility(View.VISIBLE);
+            } else {
+                ((TextView) v.findViewById(R.id.message_row_to_lbl)).setVisibility(View.INVISIBLE);
+            }
+
+            return v;
+        }
+    }
+
+    class DateTimePicker {
+        View view = null;
+        EditText editText = null;
+        DatePicker dataPicker = null;
+        TimePicker timePicker = null;
+
+        DateTimePicker(EditText ref) {
+            editText = ref;
+
+            view = getLayoutInflater().inflate(R.layout.date_time_picker, null);
+
+            dataPicker = (DatePicker) view.findViewById(R.id.date_picker);
+            timePicker = (TimePicker) view.findViewById(R.id.time_picker);
+            timePicker.setIs24HourView(true);
+
+            String oldValue = editText.getText().toString();
+
+            if (oldValue != null && !oldValue.isEmpty() && !oldValue.equals("")) {
+                try {
+                    Calendar cal = Calendar.getInstance();
+                    cal.setTime(mSimpleDateFormat.parse(oldValue));
+                    dataPicker.updateDate(cal.get(Calendar.YEAR),
+                            cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH));
+                    timePicker.setCurrentHour(cal.get(Calendar.HOUR_OF_DAY));
+                    timePicker.setCurrentMinute(cal.get(Calendar.MINUTE));
+                } catch (ParseException e) {
+                    Logger.e(TAG, "Parse exception in DataTimePicker!");
+                }
+            }
+        }
+
+        public void pick() {
+            AlertDialog.Builder alertBuilder = new AlertDialog.Builder(MapTestActivity.this);
+            alertBuilder.setView(view);
+            alertBuilder.setTitle(getResources().getString(R.string.dialog_title_pick_date_time));
+            alertBuilder.setPositiveButton(android.R.string.ok,
+                    new DialogInterface.OnClickListener() {
+
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            int year = dataPicker.getYear();
+                            int month = dataPicker.getMonth();
+                            int day = dataPicker.getDayOfMonth();
+                            int hour = timePicker.getCurrentHour();
+                            int minute = timePicker.getCurrentMinute();
+
+                            editText.setText(mSimpleDateFormat.format(new GregorianCalendar(
+                                    year, month, day, hour, minute, 0).getTime()));
+                        }
+                    });
+            alertBuilder.setNegativeButton(android.R.string.cancel, null);
+            alertBuilder.show();
+        }
+    }
+
+    private void resetPreviewEditUi() {
+        mViewBmsgHandle.setText("");
+
+        mViewBmsgStatus.setText("");
+        mViewBmsgType.setText("");
+        mViewBmsgFolder.setText("");
+
+        mViewBmsgEncoding.setText("");
+        mViewBmsgCharset.setText("");
+        mViewBmsgLanguage.setText("");
+
+        mViewBmsgContents.setText("");
+
+        mViewBmsgOrig.setText("");
+        mViewBmsgRcpt.setText("");
+
+        mPreviewMsgHandle = null;
+        mMapBmessage = null;
+
+        invalidateOptionsMenu();
+    }
+
+    private void updateUi(boolean invalidateOptionsMenu) {
+        if (mListTouchables == null) {
+            LinearLayout lay = (LinearLayout) findViewById(R.id.maptest_tab_list);
+            mListTouchables = lay.getTouchables();
+        }
+
+        for (View view : mListTouchables) {
+            view.setEnabled(mProfileService.getMapClient(mMasInstanceId).getState() == BluetoothMasClient.ConnectionState.CONNECTED
+                    && mCurrentJob == Job.IDLE);
+        }
+
+        if (invalidateOptionsMenu) {
+            invalidateOptionsMenu();
+        }
+    }
+
+    private void goToState(Job job) {
+        Logger.v(TAG, "Switch to " + job + " state from " + mCurrentJob.toString() + ".");
+        mCurrentJob = job;
+        updateUi(true);
+    }
+
+    private void updateMessage(BluetoothMapBmessage message) {
+        StringBuilder sb;
+
+        mPreviewMsgHandle = mPendingGetMessageHandle;
+        mPendingGetMessageHandle = null;
+
+        mMapBmessage = message;
+
+        boolean isRead = mMapBmessage.getStatus().equals(BluetoothMapBmessage.Status.READ);
+        ((Button) (findViewById(R.id.map_msg_set_status))).setText(
+                isRead ? getString(R.string.map_set_unread) : getString(R.string.map_set_read));
+
+        mActionBar.selectTab(mActionBar.getTabAt(1));
+
+        mViewBmsgHandle.setText(mPreviewMsgHandle);
+
+        mViewBmsgStatus.setText(message.getStatus().toString());
+        mViewBmsgType.setText(message.getType().toString());
+        mViewBmsgFolder.setText(message.getFolder());
+
+        mViewBmsgEncoding.setText(message.getEncoding());
+        mViewBmsgCharset.setText(message.getCharset());
+        mViewBmsgLanguage.setText(message.getLanguage());
+
+        mViewBmsgContents.setText(message.getBodyContent());
+
+        sb = new StringBuilder();
+        for (VCardEntry vcard : message.getOriginators()) {
+            sb.append(vcard.getDisplayName());
+            if (message.getType().equals(BluetoothMapBmessage.Type.EMAIL)) {
+                if (vcard.getEmailList() != null && vcard.getEmailList().size() > 0) {
+                    sb.append(" <").append(vcard.getEmailList().get(0).getAddress()).append(">");
+                }
+            } else {
+                if (vcard.getPhoneList() != null && vcard.getPhoneList().size() > 0) {
+                    sb.append(" <").append(vcard.getPhoneList().get(0).getNumber()).append(">");
+                }
+            }
+            sb.append(", ");
+        }
+        mViewBmsgOrig.setText(sb.toString());
+
+        sb = new StringBuilder();
+        for (VCardEntry vcard : message.getRecipients()) {
+            sb.append(vcard.getDisplayName());
+            if (message.getType().equals(BluetoothMapBmessage.Type.EMAIL)) {
+                if (vcard.getEmailList() != null && vcard.getEmailList().size() > 0) {
+                    sb.append(" <").append(vcard.getEmailList().get(0).getAddress()).append(">");
+                }
+            } else {
+                if (vcard.getPhoneList() != null && vcard.getPhoneList().size() > 0) {
+                    sb.append(" <").append(vcard.getPhoneList().get(0).getNumber()).append(">");
+                }
+            }
+            sb.append(", ");
+        }
+        mViewBmsgRcpt.setText(sb.toString());
+    }
+
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+        super.onCreateContextMenu(menu, v, menuInfo);
+
+        menu.add(0, 1, 0, "status -> READ");
+        menu.add(0, 2, 0, "status -> UNREAD");
+        menu.add(0, 3, 0, "status -> DELETED");
+        menu.add(0, 4, 0, "status -> UNDELETED");
+        menu.add(0, 5, 0, "get in native charset");
+    }
+
+    @Override
+    public boolean onContextItemSelected(MenuItem item) {
+        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
+
+        BluetoothMapMessage bmsg = mAdapterMessages.getItem(info.position);
+
+        switch (item.getItemId()) {
+            case 1:
+            case 2:
+                mProfileService.getMapClient(mMasInstanceId).setMessageReadStatus(bmsg.getHandle(),
+                        item.getItemId() == 1);
+                goToState(Job.SET_STATUS_READ);
+                break;
+            case 3:
+            case 4:
+                mProfileService.getMapClient(mMasInstanceId).setMessageDeletedStatus(
+                        bmsg.getHandle(), item.getItemId() == 3);
+                goToState(Job.DELETE_MESSAGE);
+                break;
+            case 5:
+                getMessage(bmsg.getHandle(), CharsetType.NATIVE, false);
+                break;
+        }
+
+        return true;
+    }
+
+    public void onClickRemovePeople(View v) {
+        if (v.getId() == R.id.bmsgedit_orig_del) {
+            new StringListDialogFragment(mEditOriginators).show(getFragmentManager(), "orig_del");
+        } else if (v.getId() == R.id.bmsgedit_rcpt_del) {
+            new StringListDialogFragment(mEditRecipients).show(getFragmentManager(), "rcpt_del");
+        }
+    }
+
+    public void onClickAddPerson(View v) {
+        if (v.getId() == R.id.bmsgedit_rcpt_add) {
+            new GetTextDialogFragment().show(getFragmentManager(), "rcpt_add");
+        } else if (v.getId() == R.id.bmsgedit_orig_add) {
+            new GetTextDialogFragment().show(getFragmentManager(), "orig_add");
+        }
+    }
+
+    @Override
+    public void onGetTextDialogPositive(DialogFragment dialog, String text) {
+        if ("rcpt_add".equals(dialog.getTag())) {
+            mEditRecipients.add(text);
+        } else if ("orig_add".equals(dialog.getTag())) {
+            mEditOriginators.add(text);
+        }
+
+        refreshEditPeople();
+    }
+
+    @Override
+    public void onStringListDialogPositive(DialogFragment dialog, ArrayList<String> elements) {
+        if ("rcpt_del".equals(dialog.getTag())) {
+            mEditRecipients.clear();
+            mEditRecipients.addAll(elements);
+        } else if ("orig_del".equals(dialog.getTag())) {
+            mEditOriginators.clear();
+            mEditOriginators.addAll(elements);
+        }
+
+        refreshEditPeople();
+    }
+
+    private void refreshEditPeople() {
+        StringBuilder sb;
+
+        sb = new StringBuilder();
+        for (String s : mEditOriginators) {
+            sb.append(s).append(", ");
+        }
+        ((TextView) findViewById(R.id.bmsgedit_orig)).setText(sb.toString());
+
+        sb = new StringBuilder();
+        for (String s : mEditRecipients) {
+            sb.append(s).append(", ");
+        }
+        ((TextView) findViewById(R.id.bmsgedit_rcpt)).setText(sb.toString());
+    }
+
+    public void onClickGetMessage(View v) {
+        String handle = mViewBmsgHandle.getText().toString();
+        int selCharset = ((RadioGroup) findViewById(R.id.map_msg_get_charset))
+                .getCheckedRadioButtonId();
+        boolean attachment = ((CheckBox) findViewById(R.id.map_msg_get_attachment)).isChecked();
+
+        CharsetType charset = (selCharset == R.id.map_msg_get_charset_native ? CharsetType.NATIVE
+                : CharsetType.UTF_8);
+
+        if (!getMessage(handle, charset, attachment)) {
+            Toast.makeText(MapTestActivity.this, "GetMessage rejected, check parameters",
+                    Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    public void onClickCopyMessage(View v) {
+        String type = mViewBmsgType.getText().toString();
+        Spinner typeView = (Spinner) findViewById(R.id.bmsgedit_type);
+
+        for (int i = 0; i < typeView.getCount(); i++) {
+            if (type.equals(typeView.getItemAtPosition(i))) {
+                typeView.setSelection(i);
+                break;
+            }
+        }
+
+        String enc = mViewBmsgEncoding.getText().toString();
+        Spinner encView = (Spinner) findViewById(R.id.bmsgedit_encoding);
+
+        for (int i = 0; i < encView.getCount(); i++) {
+            if (enc.equals(encView.getItemAtPosition(i))) {
+                encView.setSelection(i);
+                break;
+            }
+        }
+
+        ((EditText) findViewById(R.id.bmsgedit_contents)).setText(mViewBmsgContents.getText());
+    }
+
+    private void goToFolder(String dst) {
+        mSetPathQueue = new ArrayDeque<String>(Arrays.asList(dst.split("/")));
+
+        mProfileService.getMapClient(mMasInstanceId).setFolderRoot();
+    }
+
+    private boolean getMessage(String handle, CharsetType charset, boolean attachments) {
+        if (mProfileService == null || handle == null) {
+            return false;
+        }
+
+        BluetoothMasClient cli = mProfileService.getMapClient(mMasInstanceId);
+
+        if (!cli.getMessage(handle, charset, attachments)) {
+            return false;
+        }
+
+        goToState(Job.GET_MESSAGE);
+
+        mPendingGetMessageHandle = handle;
+
+        return true;
+    }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MessageFilterActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MessageFilterActivity.java
new file mode 100644
index 0000000..2bfb977
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MessageFilterActivity.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.os.Bundle;
+
+import org.codeaurora.bluetooth.bttestapp.R;
+import org.codeaurora.bluetooth.bttestapp.util.Logger;
+
+public class MessageFilterActivity extends FilterActivity {
+
+    private final String TAG = "MessageFilterActivity";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Logger.v(TAG, "onCreate()");
+        ActivityHelper.initialize(this, R.layout.activity_message_filter);
+        ActivityHelper.setActionBarTitle(this, R.string.title_message_filter);
+    }
+
+    @Override
+    protected void fillParameters() {
+        addParameter(0, R.id.param_subject);
+        addParameter(1, R.id.param_datatime);
+        addParameter(2, R.id.param_sender_name);
+        addParameter(3, R.id.param_sender_addressing);
+        addParameter(4, R.id.param_receipent_name);
+        addParameter(5, R.id.param_receipent_addressing);
+        addParameter(6, R.id.param_type);
+        addParameter(7, R.id.param_size);
+        addParameter(8, R.id.param_reception_status);
+        addParameter(9, R.id.param_text);
+        addParameter(10, R.id.param_attachment_size);
+        addParameter(11, R.id.param_priority);
+        addParameter(12, R.id.param_read);
+        addParameter(13, R.id.param_sent);
+        addParameter(14, R.id.param_protected);
+        addParameter(15, R.id.param_replayto_addressing);
+    }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MonkeyActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MonkeyActivity.java
new file mode 100644
index 0000000..e8839c1
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MonkeyActivity.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Adapter;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.TextView;
+
+public class MonkeyActivity extends Activity {
+
+    private static final String TAG = "MonkeyActivity";
+
+    public static final String ACTION_MONKEY = "com.qualcomm.bluetooth.action.MONKEY";
+
+    public static final String ACTION_MONKEY_QUERY = "com.qualcomm.bluetooth.action.MONKEY_QUERY";
+
+    public static final String EXTRA_OP = "extra.op";
+    public static final String EXTRA_DIALOG_TAG = "extra.dialogTag";
+    public static final String EXTRA_ID = "extra.id";
+    public static final String EXTRA_TEXT = "extra.text";
+
+    public static final String OP_DUMP = "dump";
+    public static final String OP_SELECT_TAB = "selectTab";
+    public static final String OP_CLICK = "click";
+    public static final String OP_LONG_CLICK = "longClick";
+    public static final String OP_SET_TEXT = "setText";
+    public static final String OP_SELECT_BY_TEXT = "selectByText";
+    public static final String OP_CLICK_BUTTON = "clickButton";
+    public static final String OP_CLICK_ACTION_BAR_MENU_BY_TEXT = "clickActionBarMenuByText";
+
+    protected Menu mActionBarMenu = null;
+
+    BroadcastReceiver mReceiver = new BroadcastReceiver() {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            Log.d(TAG, "received " + action);
+
+            if (ACTION_MONKEY.equals(action)) {
+                String op = intent.getStringExtra(EXTRA_OP);
+                String dialogTag = intent.getStringExtra(EXTRA_DIALOG_TAG);
+                String id = intent.getStringExtra(EXTRA_ID);
+                String text = intent.getStringExtra(EXTRA_TEXT);
+
+                if (OP_DUMP.equals(op)) {
+                    getViewById(dialogTag, null);
+                } else if (OP_SELECT_TAB.equals(op)) {
+                    monkeySelectTab(text);
+                } else if (OP_CLICK.equals(op)) {
+                    monkeyClick(dialogTag, id, false);
+                } else if (OP_LONG_CLICK.equals(op)) {
+                    monkeyClick(dialogTag, id, true);
+                } else if (OP_SET_TEXT.equals(op)) {
+                    monkeySetText(dialogTag, id, text);
+                } else if (OP_SELECT_BY_TEXT.equals(op)) {
+                    monkeySelectByText(dialogTag, id, text);
+                } else if (OP_CLICK_BUTTON.equals(op)) {
+                    monkeyClickButton(dialogTag, id);
+                } else if (OP_CLICK_ACTION_BAR_MENU_BY_TEXT.equals(op)) {
+                    monkeyClickActionBarMenuByText(text);
+                }
+            } else if (ACTION_MONKEY_QUERY.equals(action)) {
+                String op = intent.getStringExtra(EXTRA_OP);
+
+                if (op != null) {
+                    onMonkeyQuery(op, intent.getExtras());
+                }
+            }
+        }
+    };
+
+    @Override
+    protected void onResume() {
+        Log.v(TAG, "onResume");
+        super.onResume();
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_MONKEY);
+        filter.addAction(ACTION_MONKEY_QUERY);
+        registerReceiver(mReceiver, filter);
+    }
+
+    @Override
+    protected void onPause() {
+        Log.v(TAG, "onPause");
+        super.onPause();
+
+        unregisterReceiver(mReceiver);
+    }
+
+    private View getViewById(View view, String id, String tag, int nest) {
+        if (view == null) {
+            return null;
+        }
+
+        String tid = null;
+
+        final int vid = view.getId();
+        final Resources res = view.getResources();
+        if (vid != View.NO_ID && vid != 0 && res != null) {
+            if ((vid & 0xff000000) == 0x7f000000) {
+                tid = res.getResourceEntryName(vid);
+
+                if (id != null) {
+                    boolean match = false;
+                    if (id.equals(tid)) {
+                        if (tag == null) {
+                            match = true;
+                        } else {
+                            Object ttag = view.getTag();
+                            match = (ttag != null && ttag.toString().equals(tag));
+                        }
+                    }
+
+                    if (match) {
+                        return view;
+                    }
+                }
+            }
+        }
+
+        if (id == null) {
+            StringBuilder sb = new StringBuilder();
+
+            for (int i = 0; i < nest; i++) {
+                sb.append("  ");
+            }
+
+            sb.append(view.getClass().getSimpleName());
+
+            if (view instanceof TextView) {
+                String txt = ((TextView) view).getText().toString();
+
+                if (txt.length() > 0) {
+                    sb.append(" \"").append(txt).append("\"");
+                }
+            }
+
+            if (tid != null) {
+                sb.append(" [").append(tid);
+                if (view.getTag() != null) {
+                    sb.append("#").append(view.getTag().toString());
+                }
+                sb.append("]");
+            }
+
+            Log.v("DUMP", sb.toString());
+        }
+
+        View ret = null;
+
+        if (view instanceof ViewGroup) {
+            ViewGroup vg = (ViewGroup) view;
+
+            int count = vg.getChildCount();
+            for (int idx = 0; idx < count && ret == null; idx++) {
+                ret = getViewById(vg.getChildAt(idx), id, tag, nest + 1);
+            }
+        }
+
+        return ret;
+    }
+
+    private View getViewById(String dialogTag, String id) {
+        View root;
+        String tag = null;
+
+        if (dialogTag != null) {
+            Fragment frag = getFragmentManager().findFragmentByTag(dialogTag);
+
+            if (!(frag instanceof DialogFragment)) {
+                return null;
+            }
+
+            root = ((DialogFragment) frag).getDialog().findViewById(android.R.id.content);
+        } else {
+            root = findViewById(android.R.id.content);
+        }
+
+        if (id != null) {
+            int hashPos = id.lastIndexOf("#");
+            if (hashPos > 0) {
+                tag = id.substring(hashPos + 1);
+                id = id.substring(0, hashPos);
+            }
+        }
+
+        return getViewById(root, id, tag, 0);
+    }
+
+    private void monkeySelectTab(String text) {
+        ActionBar ab = getActionBar();
+
+        if (ab == null) {
+            Log.w(TAG, "selectTab not found");
+            return;
+        }
+
+        int count = ab.getTabCount();
+        for (int idx = 0; idx < count; idx++) {
+            ActionBar.Tab tab = ab.getTabAt(idx);
+            if (tab.getText().toString().toLowerCase().equals(text.toLowerCase())) {
+                ab.selectTab(tab);
+                return;
+            }
+        }
+    }
+
+    private void monkeyClick(String dialogTag, String id, boolean longClick) {
+        View view = getViewById(dialogTag, id);
+
+        if (view == null) {
+            Log.w(TAG, "click(" + dialogTag + ", " + id + ") not found");
+            return;
+        }
+
+        if (longClick) {
+            view.performLongClick();
+        } else {
+            view.performClick();
+        }
+    }
+
+    private void monkeySetText(String dialogTag, String id, String text) {
+        View view = getViewById(dialogTag, id);
+
+        if (view == null || !(view instanceof TextView)) {
+            Log.w(TAG, "setText(" + dialogTag + ", " + id + ") not found");
+            return;
+        }
+
+        TextView txt = (TextView) view;
+        txt.setText(text);
+    }
+
+    private void monkeySelectByText(String dialogTag, String id, String text) {
+        View view = getViewById(dialogTag, id);
+
+        if (view == null || !(view instanceof AdapterView)) {
+            Log.w(TAG, "selectByText(" + dialogTag + ", " + id + ") not found");
+            return;
+        }
+
+        @SuppressWarnings("unchecked")
+        AdapterView<Adapter> adv = (AdapterView<Adapter>) view;
+
+        int count = adv.getCount();
+        for (int idx = 0; idx < count; idx++) {
+            if (adv.getItemAtPosition(idx).toString().equals(text)) {
+                adv.setSelection(idx);
+                return;
+            }
+        }
+    }
+
+    private void monkeyClickButton(String dialogTag, String id) {
+        if (dialogTag == null) {
+            return;
+        }
+
+        int which;
+
+        try {
+            which = Integer.parseInt(id);
+        } catch (NumberFormatException e) {
+            return;
+        }
+
+        Fragment frag = getFragmentManager().findFragmentByTag(dialogTag);
+
+        if (!(frag instanceof DialogFragment)) {
+            return;
+        }
+
+        Dialog dlg = ((DialogFragment) frag).getDialog();
+
+        if (!(dlg instanceof AlertDialog)) {
+            return;
+        }
+
+        Button btn = ((AlertDialog) dlg).getButton(which);
+
+        btn.performClick();
+    }
+
+    private void monkeyClickActionBarMenuByText(String text) {
+        if (mActionBarMenu == null || text == null) {
+            return;
+        }
+
+        for (int idx = 0; idx < mActionBarMenu.size(); idx++) {
+            MenuItem item = mActionBarMenu.getItem(idx);
+
+            String title = item.getTitle().toString();
+
+            if (title.toLowerCase().equals(text.toLowerCase())) {
+                mActionBarMenu.performIdentifierAction(item.getItemId(), 0);
+                break;
+            }
+        }
+    }
+
+    protected void onMonkeyQuery(String op, Bundle params) {
+        // do nothing
+    }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/PbapTestActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/PbapTestActivity.java
new file mode 100644
index 0000000..4815411
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/PbapTestActivity.java
@@ -0,0 +1,978 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.ActionBar;
+import android.app.ActionBar.Tab;
+import android.app.ActionBar.TabListener;
+import android.app.FragmentTransaction;
+import android.bluetooth.BluetoothDevice;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.RadioButton;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.ViewFlipper;
+
+import com.android.vcard.VCardEntry;
+import org.codeaurora.bluetooth.pbapclient.BluetoothPbapCard;
+import org.codeaurora.bluetooth.pbapclient.BluetoothPbapClient;
+import org.codeaurora.bluetooth.bttestapp.R;
+import org.codeaurora.bluetooth.bttestapp.services.IPbapServiceCallback;
+import org.codeaurora.bluetooth.bttestapp.util.Logger;
+import org.codeaurora.bluetooth.bttestapp.util.MonkeyEvent;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class PbapTestActivity extends MonkeyActivity implements IBluetoothConnectionObserver {
+
+    private final String TAG = "PbapTestActivity";
+
+    /*
+     * Class constants.
+     */
+    private static final int REQUEST_CODE_GET_FILTER_FOR_DOWNLOAD_TAB = 0;
+    private static final int REQUEST_CODE_GET_FILTER_FOR_VCARD_TAB = 1;
+
+    private ArrayDeque<String> mSetPathQueue = null;
+
+    private static final short MAX_COUNT_DEFAULT_VALUE = 0;
+    private static final short OFFSET_DEFAULT_VALUE = 0;
+    private static final String HANDLE_DEFAULT_VALUE = "0.vcf";
+
+    /*
+     * Common UI.
+     */
+    private ActionBar mActionBar = null;
+    private ViewFlipper mViewFlipper = null;
+    /*
+     * Download functionality UI.
+     */
+    private Spinner mDownloadSpinner = null;
+    private RadioButton mRadioButtonVCard21 = null;
+    private RadioButton mRadioButtonVCard30 = null;
+    private EditText mEditTextDownloadMaxListCount = null;
+    private EditText mEditTextDownloadOffsetValue = null;
+    private Button mButtonFilter = null;
+    private Button mButtonDownloadSearch = null;
+
+    private ProgressBar mDownloadProgressBar = null;
+    private TextView mTextViewDownloadNothingFound = null;
+    private ListView mListViewDownloadContacts = null;
+
+    private RadioButton mRadioButtonSearchName = null;
+    private RadioButton mRadioButtonSearchNumber = null;
+    private RadioButton mRadioButtonSearchSound = null;
+    private EditText mEditTextSearchValue = null;
+    private EditText mEditTextBrowseMaxListCount = null;
+    private EditText mEditTextBrowseOffsetValue = null;
+    private RadioButton mRadioButtonOrderUnordered = null;
+    private RadioButton mRadioButtonOrderAlphabetical = null;
+    private RadioButton mRadioButtonOrderIndexed = null;
+    private RadioButton mRadioButtonOrderPhonetic = null;
+    private Button mButtonBrowseSearch = null;
+
+    private ProgressBar mBrowseProgressBar = null;
+    private TextView mTextViewBrowseNothingFound = null;
+    private ListView mListViewBrowseContacts = null;
+
+    /*
+     * vCard Details functionality.
+     */
+    private Spinner mVcardSpinner = null;
+    private VcardView mVcardView = null;
+    private EditText mEditTextHandleValue = null;
+
+    /*
+     * Download local variables.
+     */
+    private long mDownloadValueFilter = 0;
+    private byte mDownloadValueCardType = BluetoothPbapClient.VCARD_TYPE_21;
+    private int mDownloadValueMaxCount = MAX_COUNT_DEFAULT_VALUE;
+    private int mDownloadValueOffset = OFFSET_DEFAULT_VALUE;
+
+    /*
+     * Browse local variables.
+     */
+    private byte mBrowseValueOrder = BluetoothPbapClient.ORDER_BY_DEFAULT;
+    private byte mBrowseValueSearchAttr = BluetoothPbapClient.SEARCH_ATTR_NAME;
+    private int mBrowseValueMaxCount = MAX_COUNT_DEFAULT_VALUE;
+    private int mBrowseValueOffset = OFFSET_DEFAULT_VALUE;
+
+    /*
+     * Action bar tabs.
+     */
+    private ActionBar.Tab mDownloadTab = null;
+    private ActionBar.Tab mBrowseTab = null;
+    private ActionBar.Tab mVcardTab = null;
+
+    /*
+     * vCard local variables.
+     */
+    private String mVcardHandleValue = HANDLE_DEFAULT_VALUE;
+    private long mVcardValueFilter = 0;
+    private byte mVcardValueCardType = BluetoothPbapClient.VCARD_TYPE_21;
+
+    /*
+     * Download adapter.
+     */
+    private VCardEntryAdapter mVCardEntryAdapter = null;
+
+    OnItemClickListener mOnDownloadItemClickListener = new OnItemClickListener() {
+        @Override
+        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+            VCardEntry vcard = (VCardEntry) parent.getAdapter().getItem(position);
+            mVcardView.setVCardEntry(vcard);
+            mActionBar.selectTab(mVcardTab);
+        }
+    };
+
+    /*
+     * Browse adapter.
+     */
+    private BluetoothPbapCardAdapter mBluetoothPbapCardAdapter = null;
+
+    OnItemClickListener mOnBrowseItemClickListener = new OnItemClickListener() {
+        @Override
+        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+            BluetoothPbapCard pbacpCard = (BluetoothPbapCard) parent.getAdapter().getItem(position);
+            mProfileService.getPbapClient().pullVcardEntry(pbacpCard.handle);
+        }
+    };
+
+    /*
+     * PBAP Service.
+     */
+    private ProfileService mProfileService = null;
+    private final IPbapServiceCallback mPbapServiceCallback = new IPbapServiceCallback() {
+
+        @Override
+        public void onSetPhoneBookDone() {
+            if (mSetPathQueue != null && mSetPathQueue.size() > 0) {
+                String next = mSetPathQueue.removeFirst();
+
+                setPhonebookFolder(next);
+            } else {
+                new MonkeyEvent("pbap-setphonebook", true).send();
+            }
+        }
+
+        @Override
+        public void onPullPhoneBookDone(ArrayList<VCardEntry> list, int missedCalls) {
+            mVCardEntryAdapter.clear();
+            mVCardEntryAdapter.addAll(list);
+
+            stopProgressBarDownload();
+
+            /* send event for monkeyrunner */
+            MonkeyEvent evt = new MonkeyEvent("pbap-pullphonebook", true);
+            evt.addReplyParam("size", list.size());
+            for (VCardEntry card : list) {
+                evt.addExtReply(BluetoothPbapCard.jsonifyVcardEntry(card));
+            }
+            evt.send();
+
+            Toast.makeText(PbapTestActivity.this, "Missed calls=" + missedCalls, Toast.LENGTH_SHORT)
+                    .show();
+        }
+
+        @Override
+        public void onPullVcardListingDone(ArrayList<BluetoothPbapCard> list, int missedCalls) {
+            mBluetoothPbapCardAdapter.clear();
+            mBluetoothPbapCardAdapter.addAll(list);
+            stopProgressBarBrowse();
+
+            /* send event for monkeyrunner */
+            new MonkeyEvent("pbap-pullvcardlisting", true)
+                    .addReplyParam("size", list.size())
+                    .addExtReply(list)
+                    .send();
+
+            Toast.makeText(PbapTestActivity.this, "Missed calls=" + missedCalls, Toast.LENGTH_SHORT)
+                    .show();
+        }
+
+        @Override
+        public void onPullVcardEntryDone(VCardEntry vcard) {
+            Logger.v(TAG, "onReceivedPbapPullVcardEntryDone()");
+
+            /* send event for monkeyrunner */
+            new MonkeyEvent("pbap-pullvcardentry", true)
+                    .addExtReply(BluetoothPbapCard.jsonifyVcardEntry(vcard))
+                    .send();
+
+            mVcardView.setVCardEntry(vcard);
+            mActionBar.selectTab(mVcardTab);
+        }
+
+        @Override
+        public void onPullPhoneBookSizeDone(int size, int type) {
+            /* send event for monkeyrunner */
+            if (type == 0) {
+                new MonkeyEvent("pbap-pullphonebook-size", true)
+                        .addReplyParam("size", size)
+                        .send();
+            } else {
+                new MonkeyEvent("pbap-pullvcardlisting-size", true)
+                        .addReplyParam("size", size)
+                        .send();
+            }
+
+            Toast.makeText(PbapTestActivity.this, "size=" + size, Toast.LENGTH_SHORT).show();
+        }
+
+        @Override
+        public void onSetPhoneBookError() {
+            Logger.e(TAG, "Received from PBAP set phone book error.");
+
+            mSetPathQueue = null;
+
+            new MonkeyEvent("pbap-setphonebook", false).send();
+        }
+
+        @Override
+        public void onPullPhoneBookError() {
+            Logger.e(TAG, "Received from PBAP pull phone book error.");
+            stopProgressBarDownload();
+            Toast.makeText(PbapTestActivity.this, "PullPhoneBook FAILED", Toast.LENGTH_LONG).show();
+            new MonkeyEvent("pbap-pullphonebook", false).send();
+        }
+
+        @Override
+        public void onPullVcardListingError() {
+            Logger.e(TAG, "Received from PBAP listing error.");
+            stopProgressBarBrowse();
+            Toast.makeText(PbapTestActivity.this, "PullvCardListing FAILED", Toast.LENGTH_LONG)
+                    .show();
+            new MonkeyEvent("pbap-pullvcardlisting", false).send();
+        }
+
+        @Override
+        public void onPullVcardEntryError() {
+            Logger.e(TAG, "Received from PBAP vCard entry error.");
+            Toast.makeText(PbapTestActivity.this, "PullvCardEntry FAILED", Toast.LENGTH_LONG)
+                    .show();
+            new MonkeyEvent("pbap-pullvcardentry", false).send();
+        }
+
+        @Override
+        public void onPullPhoneBookSizeError() {
+            Logger.e(TAG, "Received from PBAP pull phone book size error.");
+            Toast.makeText(PbapTestActivity.this, "PullPhoneBook size FAILED", Toast.LENGTH_LONG)
+                    .show();
+            new MonkeyEvent("pbap-pullphonebook-size", false).send();
+        }
+
+        @Override
+        public void onPullVcardListingSizeError() {
+            Logger.e(TAG, "Received from PBAP pull vCard listing sizeerror.");
+            Toast.makeText(PbapTestActivity.this, "PullvCardListing size FAILED", Toast.LENGTH_LONG)
+                    .show();
+            new MonkeyEvent("pbap-pullvcardlisting-size", false).send();
+        }
+
+        @Override
+        public void onSessionConnected() {
+            Toast.makeText(PbapTestActivity.this, "PBAP session connected", Toast.LENGTH_SHORT)
+                    .show();
+            invalidateOptionsMenu();
+        }
+
+        @Override
+        public void onSessionDisconnected() {
+            Toast.makeText(PbapTestActivity.this, "PBAP session disconnected", Toast.LENGTH_SHORT)
+                    .show();
+            invalidateOptionsMenu();
+        }
+    };
+
+    /*
+     * PBAP Service Connection.
+     */
+    private final ServiceConnection mPbapServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            Logger.v(TAG, "onServiceConnected()");
+            mProfileService = ((ProfileService.LocalBinder) service).getService();
+            mProfileService.setPbapCallback(mPbapServiceCallback);
+
+            ProfileService.PbapSessionData pbap = mProfileService.getPbapSessionData();
+
+            if (pbap.pullPhoneBook != null) {
+                mVCardEntryAdapter.clear();
+                mVCardEntryAdapter.addAll(pbap.pullPhoneBook);
+                stopProgressBarDownload();
+            }
+
+            if (pbap.pullVcardListing != null) {
+                mBluetoothPbapCardAdapter.clear();
+                mBluetoothPbapCardAdapter.addAll(pbap.pullVcardListing);
+                stopProgressBarBrowse();
+            }
+
+            if (pbap.pullVcardEntry != null) {
+                mVcardView.setVCardEntry(pbap.pullVcardEntry);
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            Logger.v(TAG, "onServiceDisconnected()");
+            mProfileService = null;
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Logger.v(TAG, "onCreate()");
+        ActivityHelper.initialize(this, R.layout.activity_pbap_test);
+        ActivityHelper.setActionBarTitle(this, R.string.title_pbap_test);
+
+        prepareActionBar();
+        prepareUserInterfaceDownload();
+        prepareUserInterfaceBrowse();
+        prepareUserInterfaceVcard();
+
+        mBluetoothPbapCardAdapter = new BluetoothPbapCardAdapter();
+        mListViewBrowseContacts.setAdapter(mBluetoothPbapCardAdapter);
+
+        mVCardEntryAdapter = new VCardEntryAdapter();
+        mListViewDownloadContacts.setAdapter(mVCardEntryAdapter);
+
+        BluetoothConnectionReceiver.registerObserver(this);
+
+        // bind to PBAP service
+        Intent intent = new Intent(this, ProfileService.class);
+        bindService(intent, mPbapServiceConnection, BIND_AUTO_CREATE);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        Logger.v(TAG, "onStart()");
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        Logger.v(TAG, "onStop()");
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        Logger.v(TAG, "onDestroy()");
+
+        mProfileService.setPbapCallback(null);
+
+        unbindService(mPbapServiceConnection);
+        BluetoothConnectionReceiver.removeObserver(this);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        Logger.v(TAG, "onResume()");
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        Logger.v(TAG, "onPause()");
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        mActionBarMenu = menu;
+
+        getMenuInflater().inflate(R.menu.menu_pbap_test, menu);
+
+        if (mProfileService.getPbapClient().getState() != BluetoothPbapClient.ConnectionState.DISCONNECTED) {
+            menu.findItem(R.id.menu_pbap_disconnect).setVisible(true);
+        } else {
+            menu.findItem(R.id.menu_pbap_connect).setVisible(true);
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.menu_pbap_connect:
+                mProfileService.getPbapClient().connect();
+                break;
+            case R.id.menu_pbap_disconnect:
+                mProfileService.getPbapClient().disconnect();
+                break;
+            default:
+                Logger.w(TAG, "Unknown item selected.");
+                break;
+        }
+        return true;
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        Logger.v(TAG, "onActivityResult()");
+
+        if (resultCode != RESULT_OK) {
+            Logger.w(TAG, "Result code is not ok for req: " + requestCode + ".");
+            return;
+        }
+
+        if (requestCode == REQUEST_CODE_GET_FILTER_FOR_DOWNLOAD_TAB) {
+            long result = data.getLongExtra("result", -1);
+
+            if (result != -1) {
+                Logger.d(TAG, "Received download tab filter " + result + ".");
+                mDownloadValueFilter = result;
+            } else {
+                Logger.w(TAG, "Somethings go wrong here!");
+            }
+        }
+
+        if (requestCode == REQUEST_CODE_GET_FILTER_FOR_VCARD_TAB) {
+            long result = data.getLongExtra("result", -1);
+
+            if (result != -1) {
+                Logger.d(TAG, "Received vcard tab filter " + result + ".");
+                mVcardValueFilter = result;
+            } else {
+                Logger.w(TAG, "Somethings go wrong here!");
+            }
+        }
+    }
+
+    private void prepareUserInterfaceDownload() {
+        mDownloadSpinner = (Spinner) findViewById(R.id.pbap_download_spinner);
+        mButtonFilter = (Button) findViewById(R.id.pbap_download_filter_button);
+        mButtonFilter.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Logger.v(TAG, "mButtonFilter.onClick()");
+
+                Intent intent = new Intent(PbapTestActivity.this, VcardFilterActivity.class);
+                intent.putExtra("filter", mDownloadValueFilter);
+                intent.putExtra("type", mDownloadValueCardType);
+                startActivityForResult(intent, REQUEST_CODE_GET_FILTER_FOR_DOWNLOAD_TAB);
+            }
+        });
+
+        mEditTextDownloadMaxListCount = (EditText) findViewById(R.id.pbap_download_max_list_count);
+        mEditTextDownloadMaxListCount.setText(String.valueOf(mDownloadValueMaxCount));
+
+        mEditTextDownloadOffsetValue = (EditText) findViewById(R.id.pbap_download_offset_value);
+        mEditTextDownloadOffsetValue.setText(String.valueOf(mDownloadValueOffset));
+
+        mRadioButtonVCard21 = (RadioButton) findViewById(R.id.pbap_download_vcard21);
+        mRadioButtonVCard21.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Logger.v(TAG, "mRadioButonVCard21.onClick()");
+                mDownloadValueCardType = BluetoothPbapClient.VCARD_TYPE_21;
+            }
+        });
+
+        mRadioButtonVCard30 = (RadioButton) findViewById(R.id.pbap_download_vcard30);
+        mRadioButtonVCard30.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Logger.v(TAG, "mRadioButonVCard30.onClick()");
+                mDownloadValueCardType = BluetoothPbapClient.VCARD_TYPE_30;
+            }
+        });
+
+        mButtonDownloadSearch = (Button) findViewById(R.id.pbap_download_search);
+        mButtonDownloadSearch.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Logger.v(TAG, "mButtonDownloadSearch.onClick()");
+                runPbapTestDownload();
+            }
+        });
+
+        mDownloadProgressBar = (ProgressBar) findViewById(R.id.pbap_download_progress_bar);
+        mTextViewDownloadNothingFound = (TextView) findViewById(R.id.pbap_download_test_list_empty);
+        mListViewDownloadContacts = (ListView) findViewById(R.id.pbap_download_listview_contacts_list);
+        mListViewDownloadContacts.setOnItemClickListener(mOnDownloadItemClickListener);
+
+        mDownloadProgressBar.setVisibility(View.GONE);
+        mListViewDownloadContacts.setVisibility(View.GONE);
+    }
+
+    private void prepareUserInterfaceBrowse() {
+        mRadioButtonSearchName = (RadioButton) findViewById(R.id.pbap_browse_name);
+        mRadioButtonSearchName.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Logger.v(TAG, "mRadioButtonSearchName.onClick()");
+                mBrowseValueSearchAttr = BluetoothPbapClient.SEARCH_ATTR_NAME;
+            }
+        });
+
+        mRadioButtonSearchNumber = (RadioButton) findViewById(R.id.pbap_browse_number);
+        mRadioButtonSearchNumber.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Logger.v(TAG, "mRadioButtonSearchNumber.onClick()");
+                mBrowseValueSearchAttr = BluetoothPbapClient.SEARCH_ATTR_NUMBER;
+            }
+        });
+
+        mRadioButtonSearchSound = (RadioButton) findViewById(R.id.pbap_browse_sound);
+        mRadioButtonSearchSound.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Logger.v(TAG, "mRadioButtonSearchSound.onClick()");
+                mBrowseValueSearchAttr = BluetoothPbapClient.SEARCH_ATTR_SOUND;
+            }
+        });
+
+        mEditTextSearchValue = (EditText) findViewById(R.id.pbap_browse_search_value);
+
+        mEditTextBrowseMaxListCount = (EditText) findViewById(R.id.pbap_browse_max_list_count);
+        mEditTextBrowseMaxListCount.setText(String.valueOf(mBrowseValueMaxCount));
+
+        mEditTextBrowseOffsetValue = (EditText) findViewById(R.id.pbap_browse_offset_value);
+        mEditTextBrowseOffsetValue.setText(String.valueOf(mBrowseValueOffset));
+
+        mRadioButtonOrderUnordered = (RadioButton) findViewById(R.id.pbap_browse_unordered);
+        mRadioButtonOrderUnordered.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Logger.v(TAG, "mRadioButtonOrderUnordered.onClick()");
+                mBrowseValueOrder = BluetoothPbapClient.ORDER_BY_DEFAULT;
+            }
+        });
+
+        mRadioButtonOrderAlphabetical = (RadioButton) findViewById(R.id.pbap_browse_alphabetical);
+        mRadioButtonOrderAlphabetical.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Logger.v(TAG, "mRadioButtonOrderAlphabetical.onClick()");
+                mBrowseValueOrder = BluetoothPbapClient.ORDER_BY_ALPHABETICAL;
+            }
+        });
+
+        mRadioButtonOrderIndexed = (RadioButton) findViewById(R.id.pbap_browse_indexed);
+        mRadioButtonOrderIndexed.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Logger.v(TAG, "mRadioButtonOrderIndexed.onClick()");
+                mBrowseValueOrder = BluetoothPbapClient.ORDER_BY_INDEXED;
+            }
+        });
+
+        mRadioButtonOrderPhonetic = (RadioButton) findViewById(R.id.pbap_browse_phonetic);
+        mRadioButtonOrderPhonetic.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Logger.v(TAG, "mRadioButtonOrderPhonetic.onClick()");
+                mBrowseValueOrder = BluetoothPbapClient.ORDER_BY_PHONETIC;
+            }
+        });
+
+        mButtonBrowseSearch = (Button) findViewById(R.id.pbap_browse_search);
+        mButtonBrowseSearch.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Logger.v(TAG, "mButtonBrowseSearch.onClick()");
+                runPbapTestBrowse();
+            }
+        });
+
+        mBrowseProgressBar = (ProgressBar) findViewById(R.id.pbap_browse_progress_bar);
+        mTextViewBrowseNothingFound = (TextView) findViewById(R.id.pbap_browse_test_list_empty);
+        mListViewBrowseContacts = (ListView) findViewById(R.id.pbap_browse_listview_contacts_list);
+        mListViewBrowseContacts.setOnItemClickListener(mOnBrowseItemClickListener);
+
+        mBrowseProgressBar.setVisibility(View.GONE);
+        mListViewBrowseContacts.setVisibility(View.GONE);
+    }
+
+    private void prepareUserInterfaceVcard() {
+        mVcardSpinner = (Spinner) findViewById(R.id.pbap_vcard_spinner);
+        mVcardView = (VcardView) findViewById(R.id.pbap_vcard_vcardview);
+
+        mEditTextHandleValue = (EditText) findViewById(R.id.pbap_vcard_header);
+        mEditTextHandleValue.setText(mVcardHandleValue);
+    }
+
+    private void prepareActionBar() {
+        mActionBar = getActionBar();
+
+        if (mActionBar != null) {
+            mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+            mDownloadTab = mActionBar.newTab().setText(R.string.download);
+            mBrowseTab = mActionBar.newTab().setText(R.string.browse);
+            mVcardTab = mActionBar.newTab().setText(R.string.vcard);
+
+            mViewFlipper = (ViewFlipper) findViewById(R.id.pbap_test_viewflipper);
+
+            mDownloadTab.setTabListener(new TabListener() {
+                @Override
+                public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+                    /* NoP */
+                }
+
+                @Override
+                public void onTabSelected(Tab tab, FragmentTransaction ft) {
+                    mViewFlipper.setDisplayedChild(0);
+                }
+
+                @Override
+                public void onTabReselected(Tab tab, FragmentTransaction ft) {
+                    /* NoP */
+                }
+            });
+
+            mBrowseTab.setTabListener(new TabListener() {
+                @Override
+                public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+                    /* NoP */
+                }
+
+                @Override
+                public void onTabSelected(Tab tab, FragmentTransaction ft) {
+                    mViewFlipper.setDisplayedChild(1);
+                }
+
+                @Override
+                public void onTabReselected(Tab tab, FragmentTransaction ft) {
+                    /* NoP */
+                }
+            });
+
+            mVcardTab.setTabListener(new TabListener() {
+
+                @Override
+                public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+                    /* NoP */
+                }
+
+                @Override
+                public void onTabSelected(Tab tab, FragmentTransaction ft) {
+                    mViewFlipper.setDisplayedChild(2);
+                }
+
+                @Override
+                public void onTabReselected(Tab tab, FragmentTransaction ft) {
+                    /* NoP */
+                }
+            });
+
+            mActionBar.addTab(mDownloadTab);
+            mActionBar.addTab(mBrowseTab);
+            mActionBar.addTab(mVcardTab);
+        }
+    }
+
+    private void runPbapTestDownload() {
+        Logger.v(TAG, "runPbapTestDownload()");
+
+        try {
+            mDownloadValueMaxCount = Integer.parseInt(mEditTextDownloadMaxListCount.getText()
+                    .toString());
+        } catch (NumberFormatException e) {
+            mDownloadValueMaxCount = MAX_COUNT_DEFAULT_VALUE;
+        }
+
+        try {
+            mDownloadValueOffset = Integer.parseInt(mEditTextDownloadOffsetValue.getText()
+                    .toString());
+        } catch (NumberFormatException e) {
+            mDownloadValueOffset = OFFSET_DEFAULT_VALUE;
+        }
+
+        // Refresh UI EditTexts value if some are blank after edit.
+        mEditTextDownloadMaxListCount.setText(String.valueOf(mDownloadValueMaxCount));
+        mEditTextDownloadOffsetValue.setText(String.valueOf(mDownloadValueOffset));
+
+        try {
+            if (mProfileService.getPbapClient().pullPhoneBook(
+                    mDownloadSpinner.getSelectedItem().toString(), mDownloadValueFilter,
+                    mDownloadValueCardType, mDownloadValueMaxCount, mDownloadValueOffset)) {
+                startProgressBarDownload();
+            } else {
+                Toast.makeText(this, "PullPhoneBook FAILED", Toast.LENGTH_LONG).show();
+            }
+        } catch (IllegalArgumentException e) {
+            Toast.makeText(this,
+                    "PullPhoneBook FAILED: illegal arguments (" + e.getMessage() + ")",
+                    Toast.LENGTH_LONG).show();
+        }
+    }
+
+    private void startProgressBarDownload() {
+        mDownloadProgressBar.setVisibility(View.VISIBLE);
+        mListViewDownloadContacts.setVisibility(View.GONE);
+        mTextViewDownloadNothingFound.setVisibility(View.GONE);
+    }
+
+    private void stopProgressBarDownload() {
+        mDownloadProgressBar.setVisibility(View.GONE);
+        if (mVCardEntryAdapter.getCount() == 0) {
+            mTextViewDownloadNothingFound.setVisibility(View.VISIBLE);
+            mListViewDownloadContacts.setVisibility(View.GONE);
+        } else {
+            mTextViewDownloadNothingFound.setVisibility(View.GONE);
+            mListViewDownloadContacts.setVisibility(View.VISIBLE);
+        }
+    }
+
+    private void runPbapTestBrowse() {
+        Logger.v(TAG, "runPbapTestBrowse()");
+
+        byte order = mBrowseValueOrder;
+        String searchValue = mEditTextSearchValue.getText().toString();
+
+        try {
+            mBrowseValueMaxCount = Integer.parseInt(mEditTextBrowseMaxListCount.getText()
+                    .toString());
+        } catch (NumberFormatException e) {
+            mBrowseValueMaxCount = MAX_COUNT_DEFAULT_VALUE;
+        }
+
+        try {
+            mBrowseValueOffset = Integer.parseInt(mEditTextBrowseOffsetValue.getText().toString());
+        } catch (NumberFormatException e) {
+            mBrowseValueOffset = OFFSET_DEFAULT_VALUE;
+        }
+
+        // Refresh UI EditTexts value if some are blank after edit.
+        mEditTextBrowseMaxListCount.setText(String.valueOf(mBrowseValueMaxCount));
+        mEditTextBrowseOffsetValue.setText(String.valueOf(mBrowseValueOffset));
+
+        try {
+            boolean started;
+
+            if (searchValue != "" && !searchValue.isEmpty()) {
+                started = mProfileService.getPbapClient().pullVcardListing(null, order,
+                        mBrowseValueSearchAttr, searchValue, mBrowseValueMaxCount,
+                        mBrowseValueOffset);
+            } else {
+                started = mProfileService.getPbapClient().pullVcardListing(null, order,
+                        mBrowseValueMaxCount, mBrowseValueOffset);
+            }
+
+            if (started) {
+                startProgressBarBrowse();
+            } else {
+                Toast.makeText(this, "PullvCardListing FAILED", Toast.LENGTH_LONG).show();
+            }
+        } catch (IllegalArgumentException e) {
+            Toast.makeText(this,
+                    "PullvCardListing FAILED: illegal arguments (" + e.getMessage() + ")",
+                    Toast.LENGTH_LONG).show();
+        }
+    }
+
+    private void startProgressBarBrowse() {
+        mBrowseProgressBar.setVisibility(View.VISIBLE);
+        mListViewBrowseContacts.setVisibility(View.GONE);
+        mTextViewBrowseNothingFound.setVisibility(View.GONE);
+    }
+
+    private void stopProgressBarBrowse() {
+        mBrowseProgressBar.setVisibility(View.GONE);
+        if (mBluetoothPbapCardAdapter.getCount() == 0) {
+            mTextViewBrowseNothingFound.setVisibility(View.VISIBLE);
+            mListViewBrowseContacts.setVisibility(View.GONE);
+        } else {
+            mTextViewBrowseNothingFound.setVisibility(View.GONE);
+            mListViewBrowseContacts.setVisibility(View.VISIBLE);
+        }
+    }
+
+    class VCardEntryAdapter extends ArrayAdapter<VCardEntry> {
+        VCardEntryAdapter() {
+            super(PbapTestActivity.this, android.R.layout.simple_list_item_1);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            View row = convertView;
+            boolean defaultPhoto = true;
+
+            if (row == null) {
+                LayoutInflater inflater = getLayoutInflater();
+                row = inflater.inflate(R.layout.vcard_row, null);
+            }
+
+            VCardEntry tmp = getItem(position);
+
+            ((TextView) row.findViewById(R.id.vcard_title)).setText(tmp.getDisplayName());
+
+            if (tmp.getPhotoList() != null && tmp.getPhotoList().size() > 0) {
+                byte[] data = tmp.getPhotoList().get(0).getBytes();
+                try {
+                    if (data != null) {
+                        Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
+                        ((ImageView) row.findViewById(R.id.vcard_photo)).setImageBitmap(bmp);
+                        defaultPhoto = false;
+                    }
+                } catch (IllegalArgumentException e) {
+                }
+            }
+
+            if (defaultPhoto) {
+                ((ImageView) row.findViewById(R.id.vcard_photo))
+                        .setImageResource(R.drawable.ic_contact_picture);
+            }
+
+            return row;
+        }
+    }
+
+    class BluetoothPbapCardAdapter extends ArrayAdapter<BluetoothPbapCard> {
+        BluetoothPbapCardAdapter() {
+            super(PbapTestActivity.this, android.R.layout.simple_list_item_1);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            View row = convertView;
+
+            if (row == null) {
+                LayoutInflater inflater = getLayoutInflater();
+                row = inflater.inflate(R.layout.vcard_row, null);
+            }
+
+            BluetoothPbapCard tmp = getItem(position);
+
+            StringBuilder sb = new StringBuilder();
+
+            if (tmp.firstName != null) {
+                sb.append(tmp.firstName).append(" ");
+            }
+            sb.append(tmp.lastName);
+
+            ((TextView) row.findViewById(R.id.vcard_title)).setText(sb.toString());
+            return row;
+        }
+    }
+
+    @Override
+    public void onDeviceChanged(BluetoothDevice device) {
+        Logger.v(TAG, "onDeviceChanged()");
+        /* NoP */
+    }
+
+    @Override
+    public void onDeviceDisconected() {
+        Logger.e(TAG, "BT device disconnected!");
+    }
+
+    public void onClick_download_getsize(View v) {
+        mProfileService.getPbapClient().pullPhoneBookSize(
+                mDownloadSpinner.getSelectedItem().toString());
+    }
+
+    public void onClick_browse_getsize(View v) {
+        mProfileService.getPbapClient().pullVcardListingSize("");
+    }
+
+    public void onClickVcardFilterAttributes(View v) {
+        Intent intent = new Intent(PbapTestActivity.this, VcardFilterActivity.class);
+        intent.putExtra("filter", mVcardValueFilter);
+        intent.putExtra("type", mVcardValueCardType);
+        startActivityForResult(intent, REQUEST_CODE_GET_FILTER_FOR_VCARD_TAB);
+    }
+
+    public void onClickRadioButtonVcardType21(View v) {
+        mVcardValueCardType = BluetoothPbapClient.VCARD_TYPE_21;
+    }
+
+    public void onClickRadioButtonVcardType30(View v) {
+        mVcardValueCardType = BluetoothPbapClient.VCARD_TYPE_30;
+    }
+
+    public void onClick_setPhonebook(View v) {
+        int id = getResources().getIdentifier(v.getTag().toString(), "id", getPackageName());
+
+        Spinner spinner = (Spinner) findViewById(id);
+
+        if (spinner != null) {
+            setPhonebook(spinner.getSelectedItem().toString());
+        }
+    }
+
+    public void onClickVcardButtonGet(View v) {
+        mVcardHandleValue = mEditTextHandleValue.getText().toString();
+
+        if (mVcardHandleValue.isEmpty() || mVcardHandleValue == null) {
+            mVcardHandleValue = HANDLE_DEFAULT_VALUE;
+            mEditTextHandleValue.setText(mVcardHandleValue);
+        }
+
+        mProfileService.getPbapClient().pullVcardEntry(mVcardHandleValue, mVcardValueFilter,
+                mVcardValueCardType);
+    }
+
+    private void setPhonebook(String dst) {
+        dst = dst.replaceFirst("\\.vcf$", "");
+
+        mSetPathQueue = new ArrayDeque<String>(Arrays.asList(dst.split("/")));
+
+        mProfileService.getPbapClient().setPhoneBookFolderRoot();
+    }
+
+    private void setPhonebookFolder(String folder) {
+        mProfileService.getPbapClient().setPhoneBookFolderDown(folder);
+    }
+
+    public void onClick_abort(View v) {
+        mProfileService.getPbapClient().abort();
+    }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ProfileService.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ProfileService.java
new file mode 100644
index 0000000..79d6d23
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ProfileService.java
@@ -0,0 +1,801 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+//import android.bluetooth.BluetoothHandsfreeClient;
+import android.bluetooth.BluetoothMasInstance;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProfile.ServiceListener;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.util.Log;
+
+import com.android.vcard.VCardEntry;
+import org.codeaurora.bluetooth.mapclient.BluetoothMapBmessage;
+import org.codeaurora.bluetooth.mapclient.BluetoothMapEventReport;
+import org.codeaurora.bluetooth.mapclient.BluetoothMapMessage;
+import org.codeaurora.bluetooth.mapclient.BluetoothMasClient;
+import org.codeaurora.bluetooth.pbapclient.BluetoothPbapCard;
+import org.codeaurora.bluetooth.pbapclient.BluetoothPbapClient;
+import org.codeaurora.bluetooth.bttestapp.R;
+import org.codeaurora.bluetooth.bttestapp.services.IMapServiceCallback;
+import org.codeaurora.bluetooth.bttestapp.services.IPbapServiceCallback;
+import org.codeaurora.bluetooth.bttestapp.services.PbapAuthActivity;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class ProfileService extends Service {
+
+    private final static String TAG = "ProfileService";
+
+    private static final int PBAP_AUTH_NOTIFICATION_ID = 10000;
+
+    public static final String ACTION_HFP_CONNECTION_STATE = "org.codeaurora.bluetooth.action.HFP_CONNECTION_STATE";
+
+    public static final String ACTION_PBAP_CONNECTION_STATE = "org.codeaurora.bluetooth.action.PBAP_CONNECTION_STATE";
+
+    public static final String ACTION_MAP_CONNECTION_STATE = "org.codeaurora.bluetooth.action.MAP_CONNECTION_STATE";
+
+    public static final String ACTION_MAP_NOTIFICATION_STATE = "org.codeaurora.bluetooth.action.MAP_NOTIFICATION_STATE";
+
+    public static final String EXTRA_CONNECTED = "org.codeaurora.bluetooth.extra.CONNECTED";
+
+    public static final String EXTRA_NOTIFICATION_STATE = "org.codeaurora.bluetooth.extra.NOTIFICATION_STATE";
+
+    public static final String PBAP_AUTH_ACTION_REQUEST = "org.codeaurora.bluetooth.PBAP_AUTH_ACTION_REQUEST";
+
+    public static final String PBAP_AUTH_ACTION_CANCEL = "org.codeaurora.bluetooth.PBAP_AUTH_ACTION_CANCEL";
+
+    public static final String PBAP_AUTH_ACTION_RESPONSE = "org.codeaurora.bluetooth.PBAP_AUTH_ACTION_RESPONSE";
+
+    public static final String PBAP_AUTH_ACTION_TIMEOUT = "org.codeaurora.bluetooth.PBAP_AUTH_ACTION_TIMEOUT";
+
+    public static final String PBAP_AUTH_EXTRA_KEY = "org.codeaurora.bluetooth.PBAP_AUTH_EXTRA_KEY";
+
+    public static final String ACTION_MAP_GET_MESSAGE = "org.codeaurora.bluetooth.action.MAP_GET_MESSAGE";
+
+    public static final String EXTRA_MAP_INSTANCE_ID = "org.codeaurora.bluetooth.extra.MAP_INSTANCE_ID";
+
+    public static final String EXTRA_MAP_MESSAGE_HANDLE = "org.codeaurora.bluetooth.extra.MAP_MESSAGE_HANDLE";
+
+    private BluetoothDevice mDevice = null;
+
+    private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+
+    //private BluetoothHandsfreeClient mHfpClient = null;
+
+    private BluetoothPbapClient mPbapClient = null;
+
+    private IPbapServiceCallback mPbapCallback = null;
+
+    private final PbapSessionData mPbapSessionData = new PbapSessionData();
+
+    private HashMap<Integer, BluetoothMasClient> mMapClients = null;
+
+    private HashMap<Integer, IMapServiceCallback> mMapCallbacks = null;
+
+    private HashMap<Integer, MapSessionData> mMapSessionData = null;
+
+    private MapNotificationSender mMapNotificationSender = null;
+
+    private boolean mIsBound = false;
+
+    private final IBinder mBinder = new LocalBinder();
+
+    class PbapSessionData {
+        ArrayList<VCardEntry> pullPhoneBook = null;
+        ArrayList<BluetoothPbapCard> pullVcardListing = null;
+        VCardEntry pullVcardEntry = null;
+    }
+
+    class MapSessionData {
+        ArrayList<String> getFolderListing;
+        ArrayList<BluetoothMapMessage> getMessagesListing;
+        BluetoothMapBmessage getMessage;
+    }
+
+    public class LocalBinder extends Binder {
+        ProfileService getService() {
+            return ProfileService.this;
+        }
+    }
+
+    private final Handler mPbapHandler = new Handler() {
+
+        @Override
+        public void handleMessage(Message msg) {
+            Intent intent = null;
+
+            switch (msg.what) {
+                case BluetoothPbapClient.EVENT_SESSION_CONNECTED:
+                    intent = new Intent(ACTION_PBAP_CONNECTION_STATE);
+                    intent.putExtra(EXTRA_CONNECTED, true);
+                    break;
+
+                case BluetoothPbapClient.EVENT_SESSION_DISCONNECTED:
+                    intent = new Intent(ACTION_PBAP_CONNECTION_STATE);
+                    intent.putExtra(EXTRA_CONNECTED, false);
+                    break;
+            }
+
+            if (intent != null) {
+                ProfileService.this.sendBroadcast(intent);
+            }
+
+            switch (msg.what) {
+                case BluetoothPbapClient.EVENT_PULL_PHONE_BOOK_DONE:
+                    mPbapSessionData.pullPhoneBook = (ArrayList<VCardEntry>) msg.obj;
+                    break;
+                case BluetoothPbapClient.EVENT_PULL_VCARD_LISTING_DONE:
+                    mPbapSessionData.pullVcardListing = (ArrayList<BluetoothPbapCard>) msg.obj;
+                    break;
+                case BluetoothPbapClient.EVENT_PULL_VCARD_ENTRY_DONE:
+                    mPbapSessionData.pullVcardEntry = (VCardEntry) msg.obj;
+                    break;
+            }
+
+            if (mPbapCallback == null) {
+                return;
+            }
+
+            switch (msg.what) {
+                case BluetoothPbapClient.EVENT_SET_PHONE_BOOK_DONE:
+                    mPbapCallback.onSetPhoneBookDone();
+                    break;
+                case BluetoothPbapClient.EVENT_PULL_PHONE_BOOK_DONE:
+                    mPbapCallback.onPullPhoneBookDone(mPbapSessionData.pullPhoneBook, msg.arg1);
+                    break;
+                case BluetoothPbapClient.EVENT_PULL_VCARD_LISTING_DONE:
+                    mPbapCallback.onPullVcardListingDone(mPbapSessionData.pullVcardListing,
+                            msg.arg1);
+                    break;
+                case BluetoothPbapClient.EVENT_PULL_VCARD_ENTRY_DONE:
+                    mPbapCallback.onPullVcardEntryDone(mPbapSessionData.pullVcardEntry);
+                    break;
+                case BluetoothPbapClient.EVENT_PULL_PHONE_BOOK_SIZE_DONE:
+                    mPbapCallback.onPullPhoneBookSizeDone(msg.arg1, 0);
+                    break;
+                case BluetoothPbapClient.EVENT_PULL_VCARD_LISTING_SIZE_DONE:
+                    mPbapCallback.onPullPhoneBookSizeDone(msg.arg1, 1);
+                    break;
+                case BluetoothPbapClient.EVENT_SET_PHONE_BOOK_ERROR:
+                    mPbapCallback.onSetPhoneBookError();
+                    break;
+                case BluetoothPbapClient.EVENT_PULL_PHONE_BOOK_ERROR:
+                    mPbapCallback.onPullPhoneBookError();
+                    break;
+                case BluetoothPbapClient.EVENT_PULL_VCARD_LISTING_ERROR:
+                    mPbapCallback.onPullVcardListingError();
+                    break;
+                case BluetoothPbapClient.EVENT_PULL_VCARD_ENTRY_ERROR:
+                    mPbapCallback.onPullVcardEntryError();
+                    break;
+                case BluetoothPbapClient.EVENT_PULL_PHONE_BOOK_SIZE_ERROR:
+                    mPbapCallback.onPullPhoneBookSizeError();
+                    break;
+                case BluetoothPbapClient.EVENT_PULL_VCARD_LISTING_SIZE_ERROR:
+                    mPbapCallback.onPullVcardListingSizeError();
+                    break;
+                case BluetoothPbapClient.EVENT_SESSION_CONNECTED:
+                    mPbapCallback.onSessionConnected();
+                    break;
+                case BluetoothPbapClient.EVENT_SESSION_DISCONNECTED:
+                    mPbapCallback.onSessionDisconnected();
+                    break;
+                case BluetoothPbapClient.EVENT_SESSION_AUTH_REQUESTED:
+                    createPbapAuthNotification();
+                    break;
+                case BluetoothPbapClient.EVENT_SESSION_AUTH_TIMEOUT:
+                    removePbapAuthNotification();
+                    break;
+
+                default:
+                    Log.w(TAG, "Unknown message in PBAP handler: " + msg.what);
+                    break;
+            }
+        }
+    };
+
+    private final Handler mMapHandler = new Handler() {
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void handleMessage(Message msg) {
+            IMapServiceCallback cb = mMapCallbacks.get(msg.arg2);
+
+            Log.v(TAG, "mMapHandler::handleMessage msg=" + msg.what + " status=" + msg.arg1
+                    + " instanceid=" + msg.arg2);
+
+            boolean success = (msg.arg1 == BluetoothMasClient.STATUS_OK);
+
+            if (msg.what == BluetoothMasClient.EVENT_CONNECT) {
+                Intent intent = new Intent(ACTION_MAP_CONNECTION_STATE);
+                intent.putExtra(BluetoothDevice.EXTRA_MAS_INSTANCE, new BluetoothMasInstance(
+                        msg.arg2, null, 0, 0));
+                intent.putExtra(EXTRA_CONNECTED, success);
+
+                ProfileService.this.sendBroadcast(intent);
+            } else if (msg.what == BluetoothMasClient.EVENT_SET_NOTIFICATION_REGISTRATION) {
+                Intent intent = new Intent(ACTION_MAP_NOTIFICATION_STATE);
+                intent.putExtra(BluetoothDevice.EXTRA_MAS_INSTANCE, new BluetoothMasInstance(
+                        msg.arg2, null, 0, 0));
+                intent.putExtra(EXTRA_NOTIFICATION_STATE, ((Integer) msg.obj).intValue() != 0);
+
+                ProfileService.this.sendBroadcast(intent);
+            } else if (msg.what == BluetoothMasClient.EVENT_EVENT_REPORT) {
+                BluetoothMapEventReport evt = (BluetoothMapEventReport) msg.obj;
+                mMapNotificationSender.notify(msg.arg2, evt);
+            }
+
+            MapSessionData sessionData = mMapSessionData.get(msg.arg2);
+            if (sessionData == null) {
+                sessionData = new MapSessionData();
+                mMapSessionData.put(msg.arg2, sessionData);
+            }
+
+            if (success) {
+                switch (msg.what) {
+                    case BluetoothMasClient.EVENT_GET_FOLDER_LISTING:
+                        sessionData.getFolderListing = (ArrayList<String>) msg.obj;
+                        break;
+                    case BluetoothMasClient.EVENT_GET_MESSAGES_LISTING:
+                        sessionData.getMessagesListing = (ArrayList<BluetoothMapMessage>) msg.obj;
+                        break;
+                    case BluetoothMasClient.EVENT_GET_MESSAGE:
+                        sessionData.getMessage = (BluetoothMapBmessage) msg.obj;
+                        break;
+                }
+            }
+
+            if (cb == null) {
+                Log.w(TAG, "No callback registered for MAS instance " + msg.arg2);
+                return;
+            }
+
+            switch (msg.what) {
+                case BluetoothMasClient.EVENT_CONNECT:
+                    if (success) {
+                        cb.onConnect();
+                    } else {
+                        cb.onConnectError();
+                    }
+                    break;
+                case BluetoothMasClient.EVENT_UPDATE_INBOX:
+                    if (success) {
+                        cb.onUpdateInbox();
+                    } else {
+                        cb.onUpdateInboxError();
+                    }
+                    break;
+                case BluetoothMasClient.EVENT_SET_PATH:
+                    if (success) {
+                        cb.onSetPath((String) msg.obj);
+                    } else {
+                        cb.onSetPathError((String) msg.obj);
+                    }
+                    break;
+                case BluetoothMasClient.EVENT_GET_FOLDER_LISTING:
+                    if (success && msg.obj instanceof ArrayList<?>) {
+                        cb.onGetFolderListing(sessionData.getFolderListing);
+                    } else {
+                        cb.onGetFolderListingError();
+                    }
+                    break;
+                case BluetoothMasClient.EVENT_GET_FOLDER_LISTING_SIZE:
+                    if (success && msg.obj instanceof Integer) {
+                        cb.onGetFolderListingSize((Integer) msg.obj);
+                    } else {
+                        cb.onGetFolderListingSizeError();
+                    }
+                    break;
+                case BluetoothMasClient.EVENT_GET_MESSAGES_LISTING:
+                    if (success && msg.obj instanceof ArrayList<?>) {
+                        cb.onGetMessagesListing(sessionData.getMessagesListing);
+                    } else {
+                        cb.onGetMessagesListingError();
+                    }
+                    break;
+                case BluetoothMasClient.EVENT_GET_MESSAGE:
+                    if (success && msg.obj instanceof BluetoothMapBmessage) {
+                        cb.onGetMessage(sessionData.getMessage);
+                    } else {
+                        cb.onGetMessageError();
+                    }
+                    break;
+                case BluetoothMasClient.EVENT_SET_MESSAGE_STATUS:
+                    if (success) {
+                        cb.onSetMessageStatus();
+                    } else {
+                        cb.onSetMessageStatusError();
+                    }
+                    break;
+                case BluetoothMasClient.EVENT_PUSH_MESSAGE:
+                    if (success && msg.obj instanceof String) {
+                        cb.onPushMessage((String) msg.obj);
+                    } else {
+                        cb.onPushMessageError();
+                    }
+                    break;
+                case BluetoothMasClient.EVENT_GET_MESSAGES_LISTING_SIZE:
+                    if (success && msg.obj instanceof Integer) {
+                        cb.onGetMessagesListingSize((Integer) msg.obj);
+                    } else {
+                        cb.onGetMessagesListingSizeError();
+                    }
+                    break;
+                case BluetoothMasClient.EVENT_EVENT_REPORT:
+                    BluetoothMapEventReport evt = (BluetoothMapEventReport) msg.obj;
+                    cb.onEventReport(evt);
+                    break;
+                default:
+                    Log.w(TAG, "Unknown message in MAP: " + msg.what);
+            }
+
+        }
+    };
+
+    public class MapNotificationSender {
+
+        public final int NEW_MESSAGE_NOTIFICATION_ID = 20000;
+
+        public final int DELIVERY_SUCCESS_NOTIFICATION_ID = 20001;
+
+        public final int SENDING_SUCCESS_NOTIFICATION_ID = 20002;
+
+        public final int DELIVERY_FAILURE_NOTIFICATION_ID = 20003;
+
+        public final int SENDING_FAILURE_NOTIFICATION_ID = 20004;
+
+        public final int MEMORY_FULL_NOTIFICATION_ID = 20005;
+
+        public final int MEMORY_AVAILABLE_NOTIFICATION_ID = 20006;
+
+        public final int MESSAGE_DELETED_NOTIFICATION_ID = 20007;
+
+        public final int MESSAGE_SHIFT_NOTIFICATION_ID = 20008;
+
+        private final NotificationManager mNotificationManager;
+
+        MapNotificationSender() {
+            mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+        }
+
+        public void notify(int instanceId, BluetoothMapEventReport eventReport) {
+            switch (eventReport.getType()) {
+                case NEW_MESSAGE:
+                    notifyNewMessage(instanceId, eventReport);
+                    break;
+                case DELIVERY_SUCCESS:
+                    notifyDeliverySuccess(eventReport);
+                    break;
+                case SENDING_SUCCESS:
+                    notifySendingSuccess(eventReport);
+                    break;
+                case DELIVERY_FAILURE:
+                    notifyDeliveryFailure(eventReport);
+                    break;
+                case SENDING_FAILURE:
+                    notifySendingFailure(eventReport);
+                    break;
+                case MEMORY_AVAILABLE:
+                    notifyMemoryAvailable(eventReport);
+                    break;
+                case MEMORY_FULL:
+                    notifyMemoryFull(eventReport);
+                    break;
+                case MESSAGE_DELETED:
+                    notifyMessageDeleted(eventReport);
+                    break;
+                case MESSAGE_SHIFT:
+                    notifyMessageShift(eventReport);
+                    break;
+                default:
+                    Log.e(TAG, "Unknown MAP report type (" + eventReport.getType().toString()
+                            + ")!");
+                    break;
+            }
+        }
+
+        private void send(int id, String title, String text) {
+            send(id, title, text, null);
+        }
+
+        private void send(int id, String title, String text, Intent click) {
+            Notification.Builder builder = new Notification.Builder(getApplicationContext())
+                    .setContentTitle(title)
+                    .setContentText(text)
+                    .setTicker(getString(R.string.map_report_notif_ticker))
+                    .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth)
+                    .setDefaults(Notification.DEFAULT_SOUND)
+                    .setAutoCancel(true)
+                    .setOnlyAlertOnce(true);
+
+            if (click != null) {
+                builder.setContentIntent(PendingIntent.getActivity(ProfileService.this, 0, click,
+                        PendingIntent.FLAG_UPDATE_CURRENT));
+            }
+
+            mNotificationManager.notify(id, builder.build());
+        }
+
+        private void notifyNewMessage(int instanceId, BluetoothMapEventReport eventReport) {
+            Intent click = new Intent(ProfileService.this, MapTestActivity.class);
+            click.setAction(ACTION_MAP_GET_MESSAGE);
+            click.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            click.putExtra(EXTRA_MAP_INSTANCE_ID, instanceId);
+            click.putExtra(EXTRA_MAP_MESSAGE_HANDLE, eventReport.getHandle());
+
+            send(NEW_MESSAGE_NOTIFICATION_ID,
+                    String.format(getString(R.string.map_report_notif_received, eventReport
+                            .getMsgType().toString())),
+                    String.format(getString(R.string.map_report_notif_handle,
+                            eventReport.getHandle())),
+                    click);
+        }
+
+        private void notifyDeliverySuccess(BluetoothMapEventReport eventReport) {
+            send(DELIVERY_SUCCESS_NOTIFICATION_ID,
+                    getString(R.string.map_report_notif_title_delivery_success),
+                    String.format(getString(R.string.map_report_notif_handle,
+                            eventReport.getHandle())));
+        }
+
+        private void notifySendingSuccess(BluetoothMapEventReport eventReport) {
+            send(SENDING_SUCCESS_NOTIFICATION_ID,
+                    getString(R.string.map_report_notif_title_sending_success),
+                    String.format(getString(R.string.map_report_notif_handle,
+                            eventReport.getHandle())));
+        }
+
+        private void notifyDeliveryFailure(BluetoothMapEventReport eventReport) {
+            send(DELIVERY_FAILURE_NOTIFICATION_ID,
+                    getString(R.string.map_report_notif_title_delivery_failure),
+                    String.format(getString(R.string.map_report_notif_handle,
+                            eventReport.getHandle())));
+        }
+
+        private void notifySendingFailure(BluetoothMapEventReport eventReport) {
+            send(SENDING_FAILURE_NOTIFICATION_ID,
+                    getString(R.string.map_report_notif_title_sending_failure),
+                    String.format(getString(R.string.map_report_notif_handle,
+                            eventReport.getHandle())));
+        }
+
+        private void notifyMemoryFull(BluetoothMapEventReport eventReport) {
+            send(MEMORY_FULL_NOTIFICATION_ID,
+                    getString(R.string.map_report_notif_title_memory_full),
+                    getString(R.string.blank));
+        }
+
+        private void notifyMemoryAvailable(BluetoothMapEventReport eventReport) {
+            send(MEMORY_AVAILABLE_NOTIFICATION_ID,
+                    getString(R.string.map_report_notif_title_memory_available),
+                    getString(R.string.blank));
+        }
+
+        private void notifyMessageDeleted(BluetoothMapEventReport eventReport) {
+            send(MESSAGE_DELETED_NOTIFICATION_ID,
+                    getString(R.string.map_report_notif_title_message_deleted),
+                    String.format(getString(R.string.map_report_notif_handle,
+                            eventReport.getHandle())));
+        }
+
+        private void notifyMessageShift(BluetoothMapEventReport eventReport) {
+            send(MESSAGE_SHIFT_NOTIFICATION_ID,
+                    String.format(getString(R.string.map_report_notif_shifted,
+                            eventReport.getHandle())),
+                    String.format(getString(R.string.map_report_notif_fromto,
+                            eventReport.getOldFolder(), eventReport.getFolder())));
+        }
+    }
+
+    BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            Log.d(TAG, "received " + action);
+
+            /*if (BluetoothHandsfreeClient.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
+                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+                Intent new_intent = new Intent(ACTION_HFP_CONNECTION_STATE);
+
+                if (state == BluetoothProfile.STATE_CONNECTED) {
+                    new_intent.putExtra(EXTRA_CONNECTED, true);
+                } else if (state == BluetoothProfile.STATE_DISCONNECTED) {
+                    new_intent.putExtra(EXTRA_CONNECTED, false);
+                } else {
+                    return;
+                }
+
+                ProfileService.this.sendBroadcast(new_intent);
+            } else */if (PBAP_AUTH_ACTION_RESPONSE.equals(action)) {
+                String key = intent.getStringExtra(PBAP_AUTH_EXTRA_KEY);
+                mPbapClient.setAuthResponse(key);
+            } else if (PBAP_AUTH_ACTION_CANCEL.equals(action)) {
+                mPbapClient.setAuthResponse(null);
+            } else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
+                BluetoothDevice dev = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+                if (dev.equals(mDevice)) {
+                    if (mPbapClient != null) {
+                        mPbapClient.disconnect();
+                    }
+
+                    for (BluetoothMasClient cli : mMapClients.values()) {
+                        cli.disconnect();
+                    }
+
+                    checkAndStop(false, true);
+                }
+            }
+        }
+    };
+
+/*    private final ServiceListener mHfpServiceListener = new ServiceListener() {
+        @Override
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            if (profile == BluetoothProfile.HANDSFREE_CLIENT) {
+                mHfpClient = (BluetoothHandsfreeClient) proxy;
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(int profile) {
+            if (profile == BluetoothProfile.HANDSFREE_CLIENT) {
+                mHfpClient = null;
+            }
+        }
+    };
+*/
+    private void createPbapAuthNotification() {
+        NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+
+        Intent click = new Intent(this, PbapAuthActivity.class);
+        click.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        click.setAction(PBAP_AUTH_ACTION_REQUEST);
+        click.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
+
+        Intent delete = new Intent(this, PbapAuthActivity.class);
+        delete.setAction(PBAP_AUTH_ACTION_CANCEL);
+
+        Notification no = new Notification.Builder(getApplicationContext())
+                .setContentTitle(getString(R.string.auth_notif_title))
+                .setContentText(getString(R.string.auth_notif_message, mDevice.getName()))
+                .setTicker(getString(R.string.auth_notif_ticker))
+                .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth)
+                .setContentIntent(PendingIntent.getActivity(this, 0, click, 0))
+                .setDeleteIntent(PendingIntent.getBroadcast(this, 0, delete, 0))
+                .setAutoCancel(true)
+                .setOnlyAlertOnce(true)
+                .setDefaults(Notification.DEFAULT_SOUND)
+                .build();
+
+        nm.notify(PBAP_AUTH_NOTIFICATION_ID, no);
+    }
+
+    private void removePbapAuthNotification() {
+        NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+
+        nm.cancel(PBAP_AUTH_NOTIFICATION_ID);
+
+        Intent intent = new Intent(PBAP_AUTH_ACTION_TIMEOUT);
+        sendBroadcast(intent);
+    }
+
+    private void checkAndStop(boolean unbind, boolean disconnect) {
+        boolean canStop = true;
+
+        Log.v(TAG, "checkAndStop(): unbind=" + unbind + " disconnect=" + disconnect);
+
+        if (unbind) {
+            /*if (mHfpClient != null &&
+                    mHfpClient.getConnectionState(mDevice) != BluetoothProfile.STATE_DISCONNECTED) {
+                canStop = false;
+            }*/
+
+            if (mPbapClient != null
+                    && mPbapClient.getState() != BluetoothPbapClient.ConnectionState.DISCONNECTED) {
+                canStop = false;
+            }
+
+            for (BluetoothMasClient cli : mMapClients.values()) {
+                if (cli.getState() != BluetoothMasClient.ConnectionState.DISCONNECTED) {
+                    canStop = false;
+                }
+            }
+
+            if (!canStop) {
+                Log.v(TAG, "clients are still connected, won't stop");
+            }
+        }
+
+        if (disconnect && mIsBound) {
+            canStop = false;
+            Log.v(TAG, "service is still bound, won't stop");
+        }
+
+        if (canStop) {
+            stopSelf();
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        mIsBound = true;
+
+        return mBinder;
+    }
+
+    @Override
+    public boolean onUnbind(Intent intent) {
+        mIsBound = false;
+
+        checkAndStop(true, false);
+
+        return false;
+    }
+
+    @Override
+    public void onCreate() {
+        Log.v(TAG, "onCreate");
+
+        mMapClients = new HashMap<Integer, BluetoothMasClient>();
+        mMapCallbacks = new HashMap<Integer, IMapServiceCallback>();
+        mMapSessionData = new HashMap<Integer, MapSessionData>();
+        mMapNotificationSender = new MapNotificationSender();
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(PBAP_AUTH_ACTION_RESPONSE);
+        filter.addAction(PBAP_AUTH_ACTION_CANCEL);
+        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+        //filter.addAction(BluetoothHandsfreeClient.ACTION_CONNECTION_STATE_CHANGED);
+        registerReceiver(mReceiver, filter);
+
+//        mAdapter.getProfileProxy(getApplicationContext(), mHfpServiceListener,
+  //              BluetoothProfile.HANDSFREE_CLIENT);
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.v(TAG, "onStartCommand intent=" + intent + " flags=" + Integer.toHexString(flags)
+                + " startId=" + startId);
+        return START_STICKY;
+    }
+
+    @Override
+    public void onDestroy() {
+        Log.v(TAG, "onDestroy");
+
+        //mAdapter.closeProfileProxy(BluetoothProfile.HANDSFREE_CLIENT,
+          //      mHfpClient);
+
+        unregisterReceiver(mReceiver);
+
+        if (mPbapClient != null) {
+            mPbapClient.disconnect();
+        }
+
+        for (BluetoothMasClient cli : mMapClients.values()) {
+            cli.disconnect();
+        }
+    }
+
+    public void setDevice(BluetoothDevice device) {
+        if (mDevice != null && mDevice.equals(device)) {
+            return;
+        }
+
+        /*if (mHfpClient != null) {
+            mHfpClient.disconnect(mDevice);
+        }*/
+
+        if (mPbapClient != null) {
+            mPbapClient.disconnect();
+        }
+
+        for (BluetoothMasClient cli : mMapClients.values()) {
+            cli.disconnect();
+        }
+
+        mDevice = device;
+
+        if (mDevice != null) {
+            Log.v(TAG,
+                    "Current device: address=" + mDevice.getAddress() + " name="
+                            + mDevice.getName());
+        } else {
+            Log.v(TAG, "Current device: none");
+        }
+
+        mPbapClient = null;
+        mMapClients = new HashMap<Integer, BluetoothMasClient>();
+        mMapSessionData = new HashMap<Integer, MapSessionData>();
+    }
+
+    /*public BluetoothHandsfreeClient getHfpClient() {
+        return mHfpClient;
+    }*/
+
+    public BluetoothPbapClient getPbapClient() {
+        if (mDevice == null) {
+            return null;
+        }
+
+        if (mPbapClient == null) {
+            mPbapClient = new BluetoothPbapClient(mDevice, mPbapHandler);
+        }
+
+        return mPbapClient;
+    }
+
+    public void setPbapCallback(IPbapServiceCallback callback) {
+        mPbapCallback = callback;
+    }
+
+    public PbapSessionData getPbapSessionData() {
+        return mPbapSessionData;
+    }
+
+    public void setMasInstances(ArrayList<BluetoothMasInstance> instances) {
+        for (BluetoothMasInstance inst : instances) {
+            // no need to recreate already existing MAS client
+            if (mMapClients.containsKey(inst.getId())) {
+                continue;
+            }
+
+            BluetoothMasClient client = new BluetoothMasClient(mDevice, inst, mMapHandler);
+            mMapClients.put(inst.getId(), client);
+        }
+    }
+
+    public BluetoothMasClient getMapClient(int id) {
+        return mMapClients.get(id);
+    }
+
+    public void setMapCallback(int id, IMapServiceCallback callback) {
+        mMapCallbacks.put(id, callback);
+    }
+
+    public MapSessionData getMapSessionData(int id) {
+        return mMapSessionData.get(id);
+    }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ServicesFragment.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ServicesFragment.java
new file mode 100644
index 0000000..df72d43
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ServicesFragment.java
@@ -0,0 +1,611 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.Activity;
+import android.app.ListFragment;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+//import android.bluetooth.BluetoothHandsfreeClient;
+import android.bluetooth.BluetoothMasInstance;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import org.codeaurora.bluetooth.mapclient.BluetoothMasClient;
+import org.codeaurora.bluetooth.pbapclient.BluetoothPbapClient;
+import org.codeaurora.bluetooth.bttestapp.R;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+public class ServicesFragment extends ListFragment {
+
+    private final static String TAG = "ServicesFragment";
+
+    private MainActivity mActivity;
+
+    private ServicesAdapter mAdapter;
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            boolean connState = false;
+            boolean notifState = false;
+            boolean connEvent = false;
+            boolean notifEvent = false;
+            int idx = -1;
+
+            Log.v(TAG, "mReceiver got " + action);
+
+    /*        if (ProfileService.ACTION_HFP_CONNECTION_STATE.equals(action)) {
+                connState = intent.getBooleanExtra(ProfileService.EXTRA_CONNECTED, false);
+
+                Service srv = new Service(Service.Type.HFP, null);
+                idx = mAdapter.getItemPos(srv);
+
+                if (idx < 0) {
+                    Log.w(TAG, "Cannot find HFP service item");
+                }
+
+                connEvent = true;
+
+            } else */if (ProfileService.ACTION_PBAP_CONNECTION_STATE.equals(action)) {
+                connState = intent.getBooleanExtra(ProfileService.EXTRA_CONNECTED, false);
+
+                Service srv = new Service(Service.Type.PBAP, null);
+                idx = mAdapter.getItemPos(srv);
+
+                if (idx < 0) {
+                    Log.w(TAG, "Cannot find PBAP service item");
+                }
+
+                connEvent = true;
+
+            } else if (ProfileService.ACTION_MAP_CONNECTION_STATE.equals(action)) {
+                connState = intent.getBooleanExtra(ProfileService.EXTRA_CONNECTED, false);
+                BluetoothMasInstance inst = intent
+                        .getParcelableExtra(BluetoothDevice.EXTRA_MAS_INSTANCE);
+
+                Service srv = new Service(Service.Type.MAP, inst);
+                idx = mAdapter.getItemPos(srv);
+
+                if (idx < 0) {
+                    Log.w(TAG, "Cannot find MAP service item");
+                }
+
+                connEvent = true;
+
+            } else if (ProfileService.ACTION_MAP_NOTIFICATION_STATE.equals(action)) {
+                notifState = intent.getBooleanExtra(ProfileService.EXTRA_NOTIFICATION_STATE, false);
+                BluetoothMasInstance inst = intent
+                        .getParcelableExtra(BluetoothDevice.EXTRA_MAS_INSTANCE);
+
+                Service srv = new Service(Service.Type.MAP, inst);
+                idx = mAdapter.getItemPos(srv);
+
+                if (idx < 0) {
+                    Log.w(TAG, "Cannot find MAP service item");
+                }
+
+                notifEvent = true;
+
+            }
+
+            if (idx < 0) {
+                return;
+            }
+
+            View v = (ServicesFragment.this).getListView().getChildAt(idx);
+            if (v != null) {
+                Switch swSrv = (Switch) v.findViewById(R.id.service_switch);
+                Switch swNotif = (Switch) v.findViewById(R.id.notification_switch);
+
+                if (connEvent) {
+                    swSrv.setChecked(connState);
+                    swSrv.setEnabled(true);
+                    swNotif.setEnabled(connState);
+                    swNotif.setChecked(false);
+                }
+                if (notifEvent) {
+                    swNotif.setChecked(notifState);
+                    swNotif.setEnabled(true);
+                }
+            }
+        }
+    };
+
+    static final class Service {
+
+        final Type mType;
+
+        final BluetoothMasInstance mMasInstance;
+
+        enum Type {
+       //     HFP("Hands-Free Profile (AG)"),
+            PBAP("Phone Book Access Profile (PSE)"),
+            MAP("Message Access Profile (MSE)");
+
+            final String mTitle;
+
+            Type(String title) {
+                mTitle = title;
+            }
+        }
+
+        Service(Type type, BluetoothMasInstance masInstance) {
+            mType = type;
+            mMasInstance = masInstance;
+        }
+
+        @Override
+        public boolean equals(Object srv) {
+            if (!mType.equals(((Service) srv).mType)) {
+                return false;
+            }
+
+            if (mType.equals(Type.MAP)) {
+                return mMasInstance.equals(((Service) srv).mMasInstance);
+            }
+
+            return true;
+        }
+    }
+
+    private class ServicesAdapter extends BaseAdapter implements ListAdapter,
+            OnCheckedChangeListener {
+
+        private final LayoutInflater mInflater;
+
+        private final ArrayList<Service> mServices = new ArrayList<Service>();
+
+        public ServicesAdapter(Context context) {
+            super();
+            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        }
+
+        @Override
+        public int getCount() {
+            return mServices.size();
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return mServices.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            View v = convertView;
+            boolean bluetoothOn = BluetoothAdapter.getDefaultAdapter().isEnabled();
+
+            Log.v(TAG, "getView");
+            if (v == null) {
+                v = mInflater.inflate(R.layout.service_row, parent, false);
+            }
+
+            Service srv = mServices.get(position);
+
+            TextView txtName = (TextView) v.findViewById(R.id.service_name);
+            txtName.setText(srv.mType.mTitle);
+
+            TextView txtTitle = (TextView) v.findViewById(R.id.service_title);
+            if (srv.mType.equals(Service.Type.MAP)) {
+                txtTitle.setText(srv.mMasInstance.getName());
+            } else {
+                txtTitle.setText("");
+            }
+
+            Switch swSrv = (Switch) v.findViewById(R.id.service_switch);
+            swSrv.setTag(Integer.valueOf(position));
+            swSrv.setOnCheckedChangeListener(this);
+
+            Switch swNotif = (Switch) v.findViewById(R.id.notification_switch);
+            if (srv.mType.equals(Service.Type.MAP)) {
+                swNotif.setVisibility(View.VISIBLE);
+                swNotif.setTag(Integer.valueOf(position));
+                swNotif.setOnCheckedChangeListener(this);
+            } else {
+                swNotif.setVisibility(View.INVISIBLE);
+            }
+
+            // need to update switch state on new view
+            if (mActivity.mProfileService != null) {
+                switch (srv.mType) {
+                  /*  case HFP: {
+                        BluetoothHandsfreeClient cli = mActivity.mProfileService.getHfpClient();
+
+                        if (cli == null || bluetoothOn == false) {
+                            swSrv.setChecked(false);
+                            swSrv.setEnabled(false);
+                            break;
+                        }
+
+                        switch (cli.getConnectionState(mActivity.mDevice)) {
+                            case BluetoothProfile.STATE_DISCONNECTED:
+                                swSrv.setChecked(false);
+                                swSrv.setEnabled(true);
+                                break;
+                            case BluetoothProfile.STATE_CONNECTING:
+                                swSrv.setChecked(true);
+                                swSrv.setEnabled(false);
+                                break;
+                            case BluetoothProfile.STATE_CONNECTED:
+                                swSrv.setChecked(true);
+                                swSrv.setEnabled(true);
+                                break;
+                            case BluetoothProfile.STATE_DISCONNECTING:
+                                swSrv.setChecked(false);
+                                swSrv.setEnabled(false);
+                                break;
+                        }
+
+                        break;
+                    }
+*/
+                    case PBAP: {
+                        BluetoothPbapClient cli = mActivity.mProfileService.getPbapClient();
+
+                        if (cli == null || bluetoothOn == false) {
+                            swSrv.setChecked(false);
+                            swSrv.setEnabled(false);
+                            break;
+                        }
+
+                        switch (cli.getState()) {
+                            case DISCONNECTED:
+                                swSrv.setChecked(false);
+                                swSrv.setEnabled(true);
+                                break;
+                            case CONNECTING:
+                                swSrv.setChecked(true);
+                                swSrv.setEnabled(false);
+                                break;
+                            case CONNECTED:
+                                swSrv.setChecked(true);
+                                swSrv.setEnabled(true);
+                                break;
+                            case DISCONNECTING:
+                                swSrv.setChecked(false);
+                                swSrv.setEnabled(false);
+                                break;
+                        }
+
+                        break;
+                    }
+
+                    case MAP: {
+                        BluetoothMasClient cli = mActivity.mProfileService
+                                .getMapClient(srv.mMasInstance.getId());
+
+                        swNotif.setEnabled(false);
+
+                        if (cli == null || bluetoothOn == false) {
+                            swSrv.setChecked(false);
+                            swSrv.setEnabled(false);
+                            break;
+                        }
+
+                        switch (cli.getState()) {
+                            case DISCONNECTED:
+                                swSrv.setChecked(false);
+                                swSrv.setEnabled(true);
+                                break;
+                            case CONNECTING:
+                                swSrv.setChecked(true);
+                                swSrv.setEnabled(false);
+                                break;
+                            case CONNECTED:
+                                swSrv.setChecked(true);
+                                swSrv.setEnabled(true);
+                                swNotif.setEnabled(true);
+                                break;
+                            case DISCONNECTING:
+                                swSrv.setChecked(false);
+                                swSrv.setEnabled(false);
+                                break;
+                        }
+
+                        swNotif.setChecked(cli.getNotificationRegistration());
+
+                        break;
+                    }
+                }
+            }
+
+            return v;
+        }
+
+        public int getItemPos(Service srv) {
+            for (int i = 0; i < mServices.size(); i++) {
+                if (mServices.get(i).equals(srv)) {
+                    return i;
+                }
+            }
+
+            return -1;
+        }
+
+        public void addService(Service.Type type, BluetoothMasInstance masInstance) {
+            Service srv = new Service(type, masInstance);
+
+            if (!mServices.contains(srv)) {
+                mServices.add(srv);
+            }
+
+            notifyDataSetChanged();
+        }
+
+        public void removeService(Service.Type type) {
+            for (Iterator<Service> iter = mServices.iterator(); iter.hasNext();) {
+                Service srv = iter.next();
+
+                if (type == null || srv.mType.equals(type)) {
+                    iter.remove();
+                }
+            }
+
+            notifyDataSetChanged();
+        }
+
+        @Override
+        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+            Service srv = mServices.get((Integer) buttonView.getTag());
+
+            switch (srv.mType) {
+/*                case HFP:
+                    if (isChecked) {
+                        mActivity.mProfileService.getHfpClient().connect(mActivity.mDevice);
+                        buttonView.setEnabled(false);
+                    } else {
+                        mActivity.mProfileService.getHfpClient().disconnect(mActivity.mDevice);
+                        buttonView.setEnabled(false);
+                    }
+                    break;
+*/
+                case PBAP:
+                    if (isChecked) {
+                        mActivity.mProfileService.getPbapClient().connect();
+                        buttonView.setEnabled(false);
+                    } else {
+                        mActivity.mProfileService.getPbapClient().disconnect();
+                        buttonView.setEnabled(false);
+                    }
+                    break;
+
+                case MAP:
+                    BluetoothMasClient cli = mActivity.mProfileService
+                            .getMapClient(srv.mMasInstance.getId());
+                    if (isChecked) {
+                        if (buttonView.getId() == R.id.service_switch) {
+                            cli.connect();
+                        } else {
+                            cli.setNotificationRegistration(true);
+                        }
+                        buttonView.setEnabled(false);
+                    } else {
+                        if (buttonView.getId() == R.id.service_switch) {
+                            cli.disconnect();
+                        } else {
+                            cli.setNotificationRegistration(false);
+                        }
+                        buttonView.setEnabled(false);
+                    }
+                    break;
+            }
+        }
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        mAdapter = new ServicesAdapter(getActivity());
+        setListAdapter(mAdapter);
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+
+        try {
+            mActivity = (MainActivity) activity;
+        } catch (ClassCastException e) {
+            throw new ClassCastException(this.getClass().getName()
+                    + " can only be attached to MainActivity!");
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ProfileService.ACTION_HFP_CONNECTION_STATE);
+        filter.addAction(ProfileService.ACTION_PBAP_CONNECTION_STATE);
+        filter.addAction(ProfileService.ACTION_MAP_CONNECTION_STATE);
+        filter.addAction(ProfileService.ACTION_MAP_NOTIFICATION_STATE);
+        getActivity().registerReceiver(mReceiver, filter);
+
+        mAdapter.notifyDataSetChanged();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+
+        getActivity().unregisterReceiver(mReceiver);
+    }
+
+    @Override
+    public void onListItemClick(ListView lv, View v, int position, long id) {
+        Service srv = (Service) mAdapter.getItem(position);
+        Intent intent = null;
+
+        if (BluetoothAdapter.getDefaultAdapter().isEnabled() == false) return;
+
+        switch (srv.mType) {
+            case PBAP:
+                intent = new Intent(getActivity(), PbapTestActivity.class);
+                break;
+
+/*            case HFP:
+                intent = new Intent(getActivity(), HfpTestActivity.class);
+                break;
+*/
+            case MAP:
+                intent = new Intent(getActivity(), MapTestActivity.class);
+                intent.putExtra(ProfileService.EXTRA_MAP_INSTANCE_ID, srv.mMasInstance.getId());
+                break;
+
+            default:
+                // this should never happen!
+                throw new IllegalArgumentException();
+        }
+
+        startActivity(intent);
+    }
+
+    public void addService(Service.Type type, BluetoothMasInstance masInstance) {
+        mAdapter.addService(type, masInstance);
+    }
+
+    public void removeService(Service.Type type) {
+        mAdapter.removeService(type);
+    }
+
+    public void persistServices() {
+        SharedPreferences.Editor prefs = getActivity().getPreferences(Context.MODE_PRIVATE).edit();
+
+        JSONArray json = new JSONArray();
+
+        int cnt = mAdapter.getCount();
+        for (int i = 0; i < cnt; i++) {
+            Service srv = (Service) mAdapter.getItem(i);
+
+            try {
+                JSONObject jsrv = new JSONObject();
+
+                jsrv.put("type", srv.mType);
+
+                if (srv.mMasInstance != null) {
+                    jsrv.put("i_id", srv.mMasInstance.getId());
+                    jsrv.put("i_name", srv.mMasInstance.getName());
+                    jsrv.put("i_scn", srv.mMasInstance.getChannel());
+                    jsrv.put("i_msg", srv.mMasInstance.getMsgTypes());
+                }
+
+                json.put(jsrv);
+            } catch (JSONException e) {
+            }
+        }
+
+        prefs.putString(MainActivity.PREF_SERVICES, json.toString());
+
+        prefs.commit();
+    }
+
+    public void restoreServices() {
+        String str = getActivity().getPreferences(Context.MODE_PRIVATE).getString(
+                MainActivity.PREF_SERVICES, null);
+
+        ArrayList<BluetoothMasInstance> insts = new ArrayList<BluetoothMasInstance>();
+
+        if (str == null) {
+            return;
+        }
+
+        try {
+            Object obj = new JSONTokener(str).nextValue();
+
+            if (!(obj instanceof JSONArray)) {
+                return;
+            }
+
+            JSONArray json = (JSONArray) obj;
+
+            for (int i = 0; i < json.length(); i++) {
+                JSONObject jsrv = json.getJSONObject(i);
+
+                String stype = jsrv.getString("type");
+                Service.Type type = null;
+                BluetoothMasInstance inst = null;
+
+                for (Service.Type t : Service.Type.values()) {
+                    if (t.name().equals(stype)) {
+                        type = t;
+                        break;
+                    }
+                }
+
+                if (type == null) {
+                    continue;
+                }
+
+                if (type.equals(Service.Type.MAP)) {
+                    inst = new BluetoothMasInstance(jsrv.getInt("i_id"), jsrv.getString("i_name"),
+                            jsrv.getInt("i_scn"), jsrv.getInt("i_msg"));
+                    insts.add(inst);
+                }
+
+                mAdapter.addService(type, inst);
+            }
+        } catch (JSONException e) {
+        }
+
+        mActivity.mProfileService.setMasInstances(insts);
+    }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/StringListDialogFragment.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/StringListDialogFragment.java
new file mode 100644
index 0000000..899ad7c
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/StringListDialogFragment.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+class StringListDialogFragment extends DialogFragment {
+
+    private final ArrayList<String> mElements;
+
+    private TreeSet<Integer> mSelectedItems;
+
+    private StringListDialogListener mListener;
+
+    public interface StringListDialogListener {
+        public void onStringListDialogPositive(DialogFragment dialog, ArrayList<String> elements);
+    };
+
+    public StringListDialogFragment(ArrayList<String> elements) {
+        super();
+
+        mElements = new ArrayList<String>(elements);
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        mSelectedItems = new TreeSet<Integer>();
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+        CharSequence[] elements = mElements.toArray(new CharSequence[mElements.size()]);
+
+        builder.setTitle("Select items to remove")
+                .setMultiChoiceItems(elements, null,
+                        new DialogInterface.OnMultiChoiceClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+                                if (isChecked) {
+                                    mSelectedItems.add(which);
+                                } else if (mSelectedItems.contains(which)) {
+                                    mSelectedItems.remove(which);
+                                }
+                            }
+                        })
+                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        Iterator<Integer> iter = mSelectedItems.descendingIterator();
+
+                        while (iter.hasNext()) {
+                            int idx = iter.next();
+                            mElements.remove(idx);
+                        }
+
+                        mListener.onStringListDialogPositive(StringListDialogFragment.this,
+                                mElements);
+                    }
+                })
+                .setNegativeButton(android.R.string.cancel, null);
+
+        return builder.create();
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+
+        try {
+            mListener = (StringListDialogListener) activity;
+        } catch (ClassCastException e) {
+            throw new ClassCastException(activity.toString()
+                    + " must implement StringListDialogListener");
+        }
+    }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/VcardFilterActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/VcardFilterActivity.java
new file mode 100644
index 0000000..0c26762
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/VcardFilterActivity.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.os.Bundle;
+
+import org.codeaurora.bluetooth.bttestapp.ProfileService;
+import org.codeaurora.bluetooth.bttestapp.util.Logger;
+
+public class VcardFilterActivity extends FilterActivity {
+
+    private final String TAG = "FilterChooserActivity";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Logger.v(TAG, "onCreate()");
+        ActivityHelper.initialize(this, R.layout.activity_vcard_filter);
+        ActivityHelper.setActionBarTitle(this, R.string.title_vcard_filter);
+    }
+
+    @Override
+    protected void fillParameters() {
+        addParameter(0, R.id.filter_chooser_version);
+        addParameter(1, R.id.filter_chooser_fn);
+        addParameter(2, R.id.filter_chooser_n);
+        addParameter(3, R.id.filter_chooser_photo);
+        addParameter(4, R.id.filter_chooser_bday);
+        addParameter(5, R.id.filter_chooser_adr);
+        addParameter(6, R.id.filter_chooser_label);
+        addParameter(7, R.id.filter_chooser_tel);
+        addParameter(8, R.id.filter_chooser_email);
+        addParameter(9, R.id.filter_chooser_mailer);
+        addParameter(10, R.id.filter_chooser_tz);
+        addParameter(11, R.id.filter_chooser_geo);
+        addParameter(12, R.id.filter_chooser_title);
+        addParameter(13, R.id.filter_chooser_role);
+        addParameter(14, R.id.filter_chooser_logo);
+        addParameter(15, R.id.filter_chooser_agent);
+        addParameter(16, R.id.filter_chooser_org);
+        addParameter(17, R.id.filter_chooser_note);
+        addParameter(18, R.id.filter_chooser_rev);
+        addParameter(19, R.id.filter_chooser_sound);
+        addParameter(20, R.id.filter_chooser_url);
+        addParameter(21, R.id.filter_chooser_uid);
+        addParameter(22, R.id.filter_chooser_key);
+        addParameter(23, R.id.filter_chooser_nickname);
+        addParameter(24, R.id.filter_chooser_categories);
+        addParameter(25, R.id.filter_chooser_proid);
+        addParameter(26, R.id.filter_chooser_class);
+        addParameter(27, R.id.filter_chooser_sort_string);
+        addParameter(28, R.id.filter_chooser_x_irmc_call_datetime);
+        addParameter(39, R.id.filter_chooser_filter);
+    }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/VcardView.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/VcardView.java
new file mode 100644
index 0000000..3ca4ded
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/VcardView.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.vcard.VCardEntry;
+import org.codeaurora.bluetooth.bttestapp.R;
+
+public class VcardView extends LinearLayout {
+
+    private final OnLongClickListener mLongClickListener = new OnLongClickListener() {
+
+        @Override
+        public boolean onLongClick(View v) {
+            String txt = ((TextView) v).getText().toString();
+
+            ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(
+                    Context.CLIPBOARD_SERVICE);
+
+            clipboard.setPrimaryClip(ClipData.newPlainText(txt, txt));
+
+            Toast.makeText(getContext(), "copied: " + txt, Toast.LENGTH_SHORT).show();
+
+            return true;
+        }
+    };
+
+    public VcardView(Context context) {
+        super(context);
+    }
+
+    public VcardView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public VcardView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    private static VCardEntry mVCardEntry = null;
+
+    public void setVCardEntry(VCardEntry vcard) {
+        mVCardEntry = vcard;
+        populateUi();
+    }
+
+    private void addToContainer(ViewGroup cont, String str) {
+        TextView txt = new TextView(getContext());
+        txt.setText(str);
+        txt.setTextAppearance(getContext(), android.R.style.TextAppearance_Large);
+        txt.setPadding(3, 3, 3, 3);
+        txt.setOnLongClickListener(mLongClickListener);
+        cont.addView(txt);
+    }
+
+    private void populateUi() {
+        removeAllViewsInLayout();
+
+        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
+
+        View view = inflater.inflate(R.layout.vcard_view, this);
+
+        ((ImageView) view.findViewById(R.id.contact_photo))
+                .setImageResource(R.drawable.ic_contact_picture);
+
+        if (mVCardEntry.getPhotoList() != null && mVCardEntry.getPhotoList().size() > 0) {
+            byte[] data = mVCardEntry.getPhotoList().get(0).getBytes();
+            try {
+                if (data != null) {
+                    Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
+                    ((ImageView) view.findViewById(R.id.contact_photo)).setImageBitmap(bmp);
+                }
+            } catch (IllegalArgumentException e) {
+                // don't care, will just show dummy image
+            }
+        }
+
+        ((TextView) view.findViewById(R.id.contact_display_name)).setText(mVCardEntry
+                .getDisplayName());
+
+        if (mVCardEntry.getPhoneList() != null) {
+            LinearLayout cont = (LinearLayout) view.findViewById(R.id.contact_phonelist);
+
+            for (VCardEntry.PhoneData phone : mVCardEntry.getPhoneList()) {
+                addToContainer(cont, phone.getNumber());
+            }
+        }
+
+        if (mVCardEntry.getEmailList() != null) {
+            LinearLayout cont = (LinearLayout) view.findViewById(R.id.contact_emaillist);
+
+            for (VCardEntry.EmailData email : mVCardEntry.getEmailList()) {
+                addToContainer(cont, email.getAddress());
+            }
+        }
+    }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/IMapServiceCallback.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/IMapServiceCallback.java
new file mode 100644
index 0000000..b1f4608
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/IMapServiceCallback.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp.services;
+
+import org.codeaurora.bluetooth.mapclient.BluetoothMapBmessage;
+import org.codeaurora.bluetooth.mapclient.BluetoothMapEventReport;
+import org.codeaurora.bluetooth.mapclient.BluetoothMapMessage;
+
+import java.util.ArrayList;
+
+public interface IMapServiceCallback {
+
+    public void onConnect();
+
+    public void onConnectError();
+
+    public void onUpdateInbox();
+
+    public void onUpdateInboxError();
+
+    public void onSetPath(String currentPath);
+
+    public void onSetPathError(String currentPath);
+
+    public void onGetFolderListing(ArrayList<String> folders);
+
+    public void onGetFolderListingError();
+
+    public void onGetFolderListingSize(int size);
+
+    public void onGetFolderListingSizeError();
+
+    public void onGetMessagesListing(ArrayList<BluetoothMapMessage> messages);
+
+    public void onGetMessagesListingError();
+
+    public void onGetMessage(BluetoothMapBmessage message);
+
+    public void onGetMessageError();
+
+    public void onSetMessageStatus();
+
+    public void onSetMessageStatusError();
+
+    public void onPushMessage(String handle);
+
+    public void onPushMessageError();
+
+    public void onGetMessagesListingSize(int size);
+
+    public void onGetMessagesListingSizeError();
+
+    public void onEventReport(BluetoothMapEventReport eventReport);
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/IPbapServiceCallback.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/IPbapServiceCallback.java
new file mode 100644
index 0000000..292ecc5
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/IPbapServiceCallback.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp.services;
+
+import com.android.vcard.VCardEntry;
+import org.codeaurora.bluetooth.pbapclient.BluetoothPbapCard;
+
+import java.util.ArrayList;
+
+public interface IPbapServiceCallback {
+
+    void onSetPhoneBookDone();
+
+    void onPullPhoneBookDone(ArrayList<VCardEntry> list, int missedCalls);
+
+    void onPullVcardListingDone(ArrayList<BluetoothPbapCard> list, int missedCalls);
+
+    void onPullVcardEntryDone(VCardEntry vcard);
+
+    void onPullPhoneBookSizeDone(int size, int type);
+
+    void onSetPhoneBookError();
+
+    void onPullPhoneBookError();
+
+    void onPullVcardListingError();
+
+    void onPullVcardEntryError();
+
+    void onPullPhoneBookSizeError();
+
+    void onPullVcardListingSizeError();
+
+    void onSessionConnected();
+
+    void onSessionDisconnected();
+
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/PbapAuthActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/PbapAuthActivity.java
new file mode 100644
index 0000000..4541c31
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/PbapAuthActivity.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.bttestapp.services;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+import org.codeaurora.bluetooth.bttestapp.ProfileService;
+import org.codeaurora.bluetooth.bttestapp.R;
+
+public class PbapAuthActivity extends AlertActivity implements DialogInterface.OnClickListener,
+        TextWatcher {
+
+    private static final String TAG = "PbapAuthActivity";
+
+    private static final int KEY_MAX_LENGTH = 16;
+
+    private static final int TIMEOUT_MSG = 0;
+
+    private static final int TIMEOUT_VALUE_MS = 2000;
+
+    private static final String KEY_USER_TIMEOUT = "user_timeout";
+
+    private BluetoothDevice mDevice;
+
+    private Button mOkButton;
+
+    private EditText mKeyEditText;
+
+    private TextView mMessageTextView;
+
+    private String mKey;
+
+    private boolean mTimeout = false;
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (ProfileService.PBAP_AUTH_ACTION_TIMEOUT.equals(intent.getAction())) {
+                onTimeout();
+            }
+        }
+    };
+
+    private final Handler mTimeoutHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case TIMEOUT_MSG:
+                    finish();
+                    break;
+
+                default:
+                    break;
+            }
+        }
+    };
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent intent = getIntent();
+
+        if (ProfileService.PBAP_AUTH_ACTION_REQUEST.equals(intent.getAction())) {
+            mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+            showAuthDialog();
+
+            registerReceiver(mReceiver, new IntentFilter(ProfileService.PBAP_AUTH_ACTION_TIMEOUT));
+        } else {
+            Log.e(TAG, "This activity can be also started with AUTH_ACTION_REQUEST intent");
+            finish();
+        }
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        switch (which) {
+            case DialogInterface.BUTTON_POSITIVE:
+                mKey = mKeyEditText.getText().toString();
+                onPositive();
+                break;
+
+            case DialogInterface.BUTTON_NEGATIVE:
+                onNegative();
+                break;
+
+            default:
+                break;
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mReceiver);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+
+        mTimeout = savedInstanceState.getBoolean(KEY_USER_TIMEOUT);
+
+        if (mTimeout) {
+            onTimeout();
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        outState.putBoolean(KEY_USER_TIMEOUT, mTimeout);
+    }
+
+    @Override
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+    }
+
+    @Override
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+    }
+
+    @Override
+    public void afterTextChanged(Editable s) {
+        if (s.length() > 0) {
+            mOkButton.setEnabled(true);
+        }
+    }
+
+    private void showAuthDialog() {
+        final AlertController.AlertParams p = mAlertParams;
+
+        p.mIconId = android.R.drawable.ic_dialog_info;
+        p.mTitle = getString(R.string.pbap_session_key_dialog_header);
+
+        p.mPositiveButtonText = getString(android.R.string.ok);
+        p.mPositiveButtonListener = this;
+        p.mNegativeButtonText = getString(android.R.string.cancel);
+        p.mNegativeButtonListener = this;
+
+        p.mView = createView();
+
+        setupAlert();
+
+        mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
+        mOkButton.setEnabled(false);
+    }
+
+    private View createView() {
+        View view = getLayoutInflater().inflate(R.layout.pbap_auth, null);
+
+        mMessageTextView = (TextView) view.findViewById(R.id.message);
+        mMessageTextView.setText(getString(R.string.pbap_session_key_dialog_title,
+                mDevice.getName()));
+
+        mKeyEditText = (EditText) view.findViewById(R.id.text);
+        mKeyEditText.addTextChangedListener(this);
+        mKeyEditText.setFilters(new InputFilter[] {
+                new InputFilter.LengthFilter(KEY_MAX_LENGTH)
+        });
+
+        return view;
+    }
+
+    private void onTimeout() {
+        mTimeout = true;
+
+        mMessageTextView.setText(getString(R.string.pbap_authentication_timeout_message,
+                mDevice.getName()));
+
+        mKeyEditText.setVisibility(View.GONE);
+        mKeyEditText.clearFocus();
+        mKeyEditText.removeTextChangedListener(PbapAuthActivity.this);
+
+        mOkButton.setEnabled(true);
+
+        mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
+
+        mTimeoutHandler.sendMessageDelayed(mTimeoutHandler.obtainMessage(TIMEOUT_MSG),
+                TIMEOUT_VALUE_MS);
+    }
+
+    private void onPositive() {
+        if (!mTimeout) {
+            Intent intent = new Intent(ProfileService.PBAP_AUTH_ACTION_RESPONSE);
+            intent.putExtra(ProfileService.PBAP_AUTH_EXTRA_KEY, mKey);
+            sendBroadcast(intent);
+
+            mKeyEditText.removeTextChangedListener(this);
+        }
+
+        mTimeout = false;
+        finish();
+    }
+
+    private void onNegative() {
+        Intent intent = new Intent(ProfileService.PBAP_AUTH_ACTION_CANCEL);
+        sendBroadcast(intent);
+
+        mKeyEditText.removeTextChangedListener(this);
+
+        finish();
+    }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/util/Logger.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/util/Logger.java
new file mode 100644
index 0000000..26155cc
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/util/Logger.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp.util;
+
+import android.util.Log;
+
+public class Logger {
+
+    // Android Tag for logging.
+    private static final String TAG = "NexusCarKit";
+
+    private static final boolean DEBUG = true;
+
+    private static final boolean VLOG = DEBUG && true;
+    private static final boolean DLOG = DEBUG && true;
+    private static final boolean ILOG = DEBUG && true;
+    private static final boolean WLOG = DEBUG && true;
+    private static final boolean ELOG = DEBUG && true;
+
+    /**
+     * Constructor.
+     */
+    private Logger() {
+    }
+
+    public static void v(String tag, String msg) {
+        if (VLOG) {
+            Log.v(TAG, "[" + tag + "] " + msg);
+        }
+    }
+
+    public static void d(String tag, String msg) {
+        if (DLOG) {
+            Log.d(TAG, "[" + tag + "] " + msg);
+        }
+    }
+
+    public static void i(String tag, String msg) {
+        if (ILOG) {
+            Log.i(TAG, "[" + tag + "] " + msg);
+        }
+    }
+
+    public static void w(String tag, String msg) {
+        if (WLOG) {
+            Log.w(TAG, "[" + tag + "] " + msg);
+        }
+    }
+
+    public static void e(String tag, String msg) {
+        if (ELOG) {
+            Log.e(TAG, "[" + tag + "] " + msg);
+        }
+    }
+
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/util/MonkeyEvent.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/util/MonkeyEvent.java
new file mode 100644
index 0000000..a3ee825
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/util/MonkeyEvent.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp.util;
+
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public class MonkeyEvent {
+
+    private static final String TAG = "MonkeyEventStream";
+
+    private static final String SEP = "~~";
+
+    private final String mName;
+
+    private final boolean mPass;
+
+    private HashMap<String, String> mParams = null;
+
+    private ArrayList<String> mExtReply = null;
+
+    public MonkeyEvent(String name, boolean pass) {
+        mName = name.toLowerCase();
+        mPass = pass;
+        mParams = new HashMap<String, String>();
+    }
+
+    public MonkeyEvent addReplyParam(String name, int value) {
+        addReplyParam(name, Integer.toString(value));
+        return this;
+    }
+
+    public MonkeyEvent addReplyParam(String name, long value) {
+        addReplyParam(name, Long.toString(value));
+        return this;
+    }
+
+    public MonkeyEvent addReplyParam(String name, String value) {
+        name = name.toLowerCase();
+        mParams.put(name, value);
+        return this;
+    }
+
+    public MonkeyEvent addExtReply(String s) {
+        if (s != null) {
+            if (mExtReply == null) {
+                mExtReply = new ArrayList<String>();
+            }
+
+            mExtReply.add(s);
+        }
+
+        return this;
+    }
+
+    public MonkeyEvent addExtReply(Collection<?> p) {
+        if (p != null && p.size() != 0) {
+            if (mExtReply == null) {
+                mExtReply = new ArrayList<String>();
+            }
+
+            for (Object obj : p.toArray()) {
+                mExtReply.add(obj.toString());
+            }
+        }
+
+        return this;
+    }
+
+    public void send() {
+        StringBuilder sb = new StringBuilder();
+
+        if (mExtReply == null) {
+            sb.append("EVENT");
+        } else {
+            sb.append("BEGINEXTEVENT");
+        }
+
+        sb.append(SEP);
+        sb.append(mName);
+        sb.append(SEP);
+        sb.append(mPass ? 1 : 0);
+
+        for (Map.Entry<String, String> entry : mParams.entrySet()) {
+            sb.append(SEP);
+            sb.append(entry.getKey());
+            sb.append("=");
+            sb.append(entry.getValue());
+        }
+
+        Log.i(TAG, sb.toString());
+
+        if (mExtReply != null) {
+            for (String s : mExtReply) {
+                Log.i(TAG, s);
+            }
+
+            Log.i(TAG, "ENDEXTEVENT");
+        }
+    }
+}