adding BLE tests

Change-Id: I0456cf369c536fa7f4d243eb6c4f36c78d7aa23c
(cherry picked from commit 321473715128b0297f8f37bdffbfc49ab8b7b7cc)
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 5617e26..49a3c9c 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -208,6 +208,122 @@
                 android:label="@string/bt_device_picker"
                 android:configChanges="keyboardHidden|orientation|screenSize" />
 
+        <service android:name=".bluetooth.BleClientService"
+                android:label="@string/ble_client_service_name" />
+
+        <service android:name=".bluetooth.BleServerService"
+                android:label="ble_server_service_name" />
+
+        <activity android:name=".bluetooth.BleClientTestActivity"
+                android:label="@string/ble_client_test_name"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/bt_le" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
+        </activity>
+
+        <activity android:name=".bluetooth.BleClientConnectActivity"
+                android:label="@string/ble_client_connect_name"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/bt_le" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleClientTestActivity" />
+        </activity>
+
+        <activity android:name=".bluetooth.BleDiscoverServiceActivity"
+                android:label="@string/ble_discover_service_name"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/bt_le" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleClientTestActivity" />
+        </activity>
+
+        <activity android:name=".bluetooth.BleClientCharacteristicActivity"
+                android:label="@string/ble_client_characteristic_name"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/bt_le" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleClientTestActivity" />
+        </activity>
+
+        <activity android:name=".bluetooth.BleNotifyCharacteristicActivity"
+                android:label="@string/ble_notify_characteristic_name"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/bt_le" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleClientTestActivity" />
+        </activity>
+
+        <activity android:name=".bluetooth.BleClientDescriptorActivity"
+                android:label="@string/ble_client_descriptor_name"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/bt_le" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleClientTestActivity" />
+        </activity>
+
+        <activity android:name=".bluetooth.BleReliableWriteActivity"
+                android:label="@string/ble_reliable_write_name"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/bt_le" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleClientTestActivity" />
+        </activity>
+
+        <activity android:name=".bluetooth.BleReadRssiActivity"
+                android:label="@string/ble_read_rssi_name"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/bt_le" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleClientTestActivity" />
+        </activity>
+
+        <activity android:name=".bluetooth.BleClientDisconnectActivity"
+                android:label="@string/ble_client_disconnect_name"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/bt_le" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleClientTestActivity" />
+        </activity>
+
+        <activity android:name=".bluetooth.BleServerStartActivity"
+                android:label="@string/ble_server_start_name"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/bt_le" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
+        </activity>
+
         <activity android:name=".suid.SuidFilesActivity"
                 android:label="@string/suid_files"
                 android:configChanges="keyboardHidden|orientation|screenSize">
diff --git a/apps/CtsVerifier/res/layout/ble_button.xml b/apps/CtsVerifier/res/layout/ble_button.xml
new file mode 100644
index 0000000..6428fe2
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_button.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<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="10dip"
+        >
+    <Button android:id="@+id/ble_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            />
+
+    <include android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons"
+            />
+</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/ble_client_connect.xml b/apps/CtsVerifier/res/layout/ble_client_connect.xml
new file mode 100644
index 0000000..54a0a99
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_client_connect.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<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="10dip"
+        >
+
+    <LinearLayout android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            >
+        <EditText android:id="@+id/ble_address"
+                android:layout_weight="1"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:hint="@string/ble_address"
+                />
+        <Button android:id="@+id/ble_connect"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/ble_connect"
+                />
+    </LinearLayout>
+
+    <include android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons"
+            />
+</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/ble_client_read_write.xml b/apps/CtsVerifier/res/layout/ble_client_read_write.xml
new file mode 100644
index 0000000..7edba62
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_client_read_write.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<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="10dip"
+        >
+    <LinearLayout android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            >
+        <LinearLayout android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                >
+            <EditText android:id="@+id/write_text"
+                    android:layout_width="0dp"
+                    android:layout_weight="1"
+                    android:layout_height="wrap_content"
+                    android:hint="@string/ble_write_hint"
+                    android:padding="10dip"
+                    />
+            <Button android:id="@+id/ble_write"
+                    android:layout_height="wrap_content"
+                    android:layout_width="wrap_content"
+                    android:text="@string/ble_write"
+                    />
+        </LinearLayout>
+
+        <LinearLayout android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                >
+            <TextView android:id="@+id/read_text"
+                    android:layout_width="0dp"
+                    android:layout_weight="1"
+                    android:layout_height="wrap_content"
+                    android:hint="@string/ble_read_hint"
+                    android:padding="10dip"
+                    android:textSize="18sp"
+                    />
+            <Button android:id="@+id/ble_read"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/ble_read"
+                    />
+        </LinearLayout>
+    </LinearLayout>
+
+    <include android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons"
+            />
+</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/ble_client_test.xml b/apps/CtsVerifier/res/layout/ble_client_test.xml
new file mode 100644
index 0000000..660abd5
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_client_test.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<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="10dip"
+        >
+    <ListView android:id="@+id/ble_client_tests"
+            android:layout_height="fill_parent"
+            android:layout_width="match_parent"
+            android:padding="10dip"
+            />
+
+    <include android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons"
+            />
+</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/ble_notify_characteristic.xml b/apps/CtsVerifier/res/layout/ble_notify_characteristic.xml
new file mode 100644
index 0000000..786918a
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_notify_characteristic.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<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="10dip"
+        >
+    <RelativeLayout android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            >
+        <Button android:id="@+id/ble_notify"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_centerHorizontal="true"
+                android:text="@string/ble_begin_notification"
+                android:padding="10dip"
+                />
+        <TextView android:id="@+id/ble_notify_text"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_centerHorizontal="true"
+                android:layout_below="@id/ble_notify"
+                android:textSize="20sp"
+                android:padding="10dip"
+                />
+    </RelativeLayout>
+
+    <include android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons"
+            />
+</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/ble_read_rssi.xml b/apps/CtsVerifier/res/layout/ble_read_rssi.xml
new file mode 100644
index 0000000..8aa3193
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_read_rssi.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<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="10dip"
+        >
+    <RelativeLayout android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            >
+        <Button android:id="@+id/ble_read_rssi"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_centerHorizontal="true"
+                android:text="@string/ble_read_rssi"
+                android:padding="10dip"
+                />
+        <TextView android:id="@+id/ble_rssi_text"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_centerHorizontal="true"
+                android:layout_below="@id/ble_read_rssi"
+                android:textSize="20sp"
+                android:padding="10dip"
+                />
+    </RelativeLayout>
+
+    <include android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons"
+            />
+</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/ble_reliable_write.xml b/apps/CtsVerifier/res/layout/ble_reliable_write.xml
new file mode 100644
index 0000000..7db78ff
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_reliable_write.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<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="10dip"
+        >
+    <LinearLayout android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            >
+        <EditText android:id="@+id/write_text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:hint="@string/ble_write_hint"
+                android:padding="5dip"
+                />
+        <LinearLayout android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                >
+            <Button android:id="@+id/ble_begin"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    android:text="@string/ble_begin_write"
+                    />
+            <Button android:id="@+id/ble_write"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    android:text="@string/ble_write"
+                    />
+            <Button android:id="@+id/ble_execute"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    android:text="@string/ble_execute_write"
+                    />
+        </LinearLayout>
+    </LinearLayout>
+
+    <include android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons"
+            />
+</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/ble_server_start.xml b/apps/CtsVerifier/res/layout/ble_server_start.xml
new file mode 100644
index 0000000..9ce714d
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_server_start.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<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="10dip"
+        >
+    <include android:id="@+id/pass_fail_buttons" 
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons"
+            />
+    <ListView android:id="@+id/ble_server_tests"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:layout_above="@id/pass_fail_buttons"
+            android:layout_alignParentTop="true"
+            android:padding="10dip"
+            />
+</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/ble_server_start_item.xml b/apps/CtsVerifier/res/layout/ble_server_start_item.xml
new file mode 100644
index 0000000..136192f
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_server_start_item.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="10dip"
+        android:orientation="horizontal"
+        >
+    <ImageView android:id="@+id/status"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:padding="10dip"
+            android:src="@drawable/fs_indeterminate"
+            />
+    <TextView android:id="@+id/instructions"
+            style="@style/InstructionsSmallFont"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            />
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 9b9497e..58f0103 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -119,6 +119,7 @@
 
     <string name="bt_control">Bluetooth Control</string>
     <string name="bt_device_communication">Device Communication</string>
+    <string name="bt_le">Bluetooth Low Energy</string>
 
     <string name="bt_toggle_bluetooth">Toggle Bluetooth</string>
     <string name="bt_toggle_instructions">Disable and enable Bluetooth to successfully complete this test.</string>
@@ -179,6 +180,53 @@
     <string name="bt_unpair">Device must be unpaired via Bluetooth settings before completing the test.\n\nUnpair the device in settings, make the server discoverable, and rescan to pick this device.</string>
     <string name="bt_settings">Bluetooth Settings</string>
 
+    <!-- BLE client side strings -->
+    <string name="ble_client_service_name">Bluetooth LE GATT Client Handler Service</string>
+    <string name="ble_client_test_name">BLE Client Test</string>
+    <string name="ble_client_connect_name">1. BLE Client Connect</string>
+    <string name="ble_discover_service_name">2. BLE Discover Service</string>
+    <string name="ble_client_characteristic_name">3. BLE Read/Write Characteristic</string>
+    <string name="ble_reliable_write_name">4. BLE Reliable Write</string>
+    <string name="ble_notify_characteristic_name">5. BLE Notify Characteristic</string>
+    <string name="ble_client_descriptor_name">6. BLE Read/Write Descriptor</string>
+    <string name="ble_read_rssi_name">7. BLE Read RSSI</string>
+    <string name="ble_client_disconnect_name">8. BLE Client Disconnect</string>
+    <string name="ble_client_test_info">The BLE test must be done simultaneously on two devices. This device is the client. All tests listed here must be done in order.</string>
+    <string name="ble_client_send_connect_info">Type in the Bluetooth address of the remote device to connect to, and verify that the devices are connected.</string>
+    <string name="ble_discover_service_info">Verify that the service is discovered when you press the "Discover Service" button.</string>
+    <string name="ble_read_write_info">Write values to and read values from the server to verify that the write and read functionalities are working correctly.</string>
+    <string name="ble_reliable_write_info">A Reliable Write has two steps.\n\n1) Write to the device. This will trigger a callback from the server to verify that the value written was correct.\n2) Execute the write, if the value written is valid.</string>
+    <string name="ble_notify_characteristic_info">Start accepting notifications, and verify that notifications are being reported correctly. The server should be notifying this device with the time every second.</string>
+    <string name="ble_read_rssi_info">Press button to read the RSSI value. Verify that the RSSI changes as you move the two devices further apart or closer together.</string>
+    <string name="ble_client_disconnect_info">Verify that the device is disconnected when you press the "Disconnect" button</string>
+    <string name="ble_address">Bluetooth address</string>
+    <string name="ble_connect">Connect</string>
+    <string name="ble_discover_service">Discover service</string>
+    <string name="ble_write_hint">Nothing to write yet</string>
+    <string name="ble_read_hint">Nothing read yet</string>
+    <string name="ble_write">Write</string>
+    <string name="ble_read">Read</string>
+    <string name="ble_begin_write">Begin write</string>
+    <string name="ble_execute_write">Execute write</string>
+    <string name="ble_begin_notification">Begin notification</string>
+    <string name="ble_stop_notification">Stop notification</string>
+    <string name="ble_waiting_notification">Waiting on notification</string>
+    <string name="ble_read_rssi">Read RSSI</string>
+    <string name="ble_disconnect">Disconnect</string>
+
+    <!-- BLE server side strings -->
+    <string name="ble_server_service_name">Bluetooth LE GATT Server Handler Service</string>
+    <string name="ble_server_start_name">BLE Server Test</string>
+    <string name="ble_server_start_info">The BLE test must be done simultaneously on two devices, a server device and a client device. This device is the server.</string>
+    <string name="ble_server_receiving_connect">Waiting on connection from BLE client.</string>
+    <string name="ble_server_add_service">Adding service to BLE server.</string>
+    <string name="ble_server_write_characteristic">Waiting on write characteristic request</string>
+    <string name="ble_server_read_characteristic">Waiting on read characteristic request</string>
+    <string name="ble_server_write_descriptor">Waiting on write descriptor request</string>
+    <string name="ble_server_read_descriptor">Waiting on read descriptor request</string>
+    <string name="ble_server_reliable_write">Waiting on reliable write from client</string>
+    <string name="ble_server_receiving_disconnect">Waiting on disconnection from BLE client</string>
+
     <!-- Strings for FeatureSummaryActivity -->
     <string name="feature_summary">Hardware/Software Feature Summary</string>
     <string name="feature_summary_info">This is a test for...</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleButtonActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleButtonActivity.java
new file mode 100644
index 0000000..19900af
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleButtonActivity.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.Toast;
+
+public class BleButtonActivity extends PassFailButtons.Activity {
+
+    static final int DISCOVER_SERVICE = 0;
+    static final int DISCONNECT = 1;
+
+    private int mName;
+    private int mInfo;
+    private int mButtonText;
+    private int mCommand;
+    private String mFilter;
+    private String mMessage;
+
+    BleButtonActivity(int target) {
+        if (target == DISCOVER_SERVICE) {
+            mName = R.string.ble_discover_service_name;
+            mInfo = R.string.ble_discover_service_info;
+            mButtonText = R.string.ble_discover_service;
+            mCommand = BleClientService.COMMAND_DISCOVER_SERVICE;
+            mFilter = BleClientService.BLE_SERVICES_DISCOVERED;
+            mMessage = "Service discovered.";
+        } else if (target == DISCONNECT) {
+            mName = R.string.ble_client_disconnect_name;
+            mInfo = R.string.ble_client_disconnect_name;
+            mButtonText = R.string.ble_disconnect;
+            mCommand = BleClientService.COMMAND_DISCONNECT;
+            mFilter = BleClientService.BLE_BLUETOOTH_DISCONNECTED;
+            mMessage = "Bluetooth LE disconnected.";
+        }
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ble_button);
+        setPassFailButtonClickListeners();
+        setInfoResources(mName, mInfo, -1);
+        getPassButton().setEnabled(false);
+
+        ((Button) findViewById(R.id.ble_button)).setText(mButtonText);
+        ((Button) findViewById(R.id.ble_button)).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent intent = new Intent(BleButtonActivity.this, BleClientService.class);
+                intent.putExtra(BleClientService.EXTRA_COMMAND, mCommand);
+                startService(intent);
+            }
+        });
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(mFilter);
+        registerReceiver(onBroadcast, filter);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        unregisterReceiver(onBroadcast);
+    }
+
+    private void showMessage(String msg) {
+        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
+    }
+
+    private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            showMessage(mMessage);
+            getPassButton().setEnabled(true);
+        }
+    };
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientCharacteristicActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientCharacteristicActivity.java
new file mode 100644
index 0000000..1e1941f
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientCharacteristicActivity.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+public class BleClientCharacteristicActivity extends BleReadWriteActivity {
+    public BleClientCharacteristicActivity() {
+        super(BleReadWriteActivity.CHARACTERISTIC);
+    }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientConnectActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientConnectActivity.java
new file mode 100644
index 0000000..a3a9830
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientConnectActivity.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+
+public class BleClientConnectActivity extends PassFailButtons.Activity {
+
+    private EditText mEditText;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ble_client_connect);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.ble_client_connect_name,
+                         R.string.ble_client_send_connect_info, -1);
+        getPassButton().setEnabled(false);
+
+        mEditText = (EditText) findViewById(R.id.ble_address);
+
+        ((Button) findViewById(R.id.ble_connect)).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                String address = mEditText.getText().toString();
+                if (!BluetoothAdapter.checkBluetoothAddress(address)) {
+                    showMessage("Invalid bluetooth address.");
+                } else {
+                    Intent intent = new Intent(BleClientConnectActivity.this,
+                                               BleClientService.class);
+                    intent.putExtra(BleClientService.EXTRA_COMMAND,
+                                    BleClientService.COMMAND_CONNECT);
+                    intent.putExtra(BluetoothDevice.EXTRA_DEVICE, address);
+                    startService(intent);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BleClientService.BLE_BLUETOOTH_CONNECTED);
+        registerReceiver(onBroadcast, filter);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        unregisterReceiver(onBroadcast);
+    }
+
+    private void showMessage(String msg) {
+        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
+    }
+
+    private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            showMessage("Bluetooth LE connected");
+            getPassButton().setEnabled(true);
+        }
+    };
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientDescriptorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientDescriptorActivity.java
new file mode 100644
index 0000000..ab2229a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientDescriptorActivity.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+public class BleClientDescriptorActivity extends BleReadWriteActivity {
+    public BleClientDescriptorActivity() {
+        super(BleReadWriteActivity.DESCRIPTOR);
+    }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientDisconnectActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientDisconnectActivity.java
new file mode 100644
index 0000000..083d327
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientDisconnectActivity.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+public class BleClientDisconnectActivity extends BleButtonActivity {
+    public BleClientDisconnectActivity() {
+        super(BleButtonActivity.DISCONNECT);
+    }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java
new file mode 100644
index 0000000..556ad06
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import java.util.UUID;
+import java.util.List;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+import android.widget.Toast;
+
+public class BleClientService extends Service {
+
+    public static final boolean DEBUG = true;
+    public static final String TAG = "BleClientService";
+
+    public static final int COMMAND_CONNECT = 0;
+    public static final int COMMAND_DISCONNECT = 1;
+    public static final int COMMAND_DISCOVER_SERVICE = 2;
+    public static final int COMMAND_READ_RSSI = 3;
+    public static final int COMMAND_WRITE_CHARACTERISTIC = 4;
+    public static final int COMMAND_READ_CHARACTERISTIC = 5;
+    public static final int COMMAND_WRITE_DESCRIPTOR = 6;
+    public static final int COMMAND_READ_DESCRIPTOR = 7;
+    public static final int COMMAND_SET_NOTIFICATION = 8;
+    public static final int COMMAND_BEGIN_WRITE = 9;
+    public static final int COMMAND_EXECUTE_WRITE = 10;
+    public static final int COMMAND_ABORT_RELIABLE = 11;
+
+    public static final String BLE_BLUETOOTH_CONNECTED =
+            "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_CONNECTED";
+    public static final String BLE_BLUETOOTH_DISCONNECTED =
+            "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_DISCONNECTED";
+    public static final String BLE_SERVICES_DISCOVERED =
+            "com.android.cts.verifier.bluetooth.BLE_SERVICES_DISCOVERED";
+    public static final String BLE_CHARACTERISTIC_READ =
+            "com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_READ";
+    public static final String BLE_CHARACTERISTIC_WRITE =
+            "com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_WRITE";
+    public static final String BLE_CHARACTERISTIC_CHANGED =
+            "com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_CHANGED";
+    public static final String BLE_DESCRIPTOR_READ =
+            "com.android.cts.verifier.bluetooth.BLE_DESCRIPTOR_READ";
+    public static final String BLE_DESCRIPTOR_WRITE =
+            "com.android.cts.verifier.bluetooth.BLE_DESCRIPTOR_WRITE";
+    public static final String BLE_RELIABLE_WRITE_COMPLETED =
+            "com.android.cts.verifier.bluetooth.BLE_RELIABLE_WRITE_COMPLETED";
+    public static final String BLE_READ_REMOTE_RSSI =
+            "com.android.cts.verifier.bluetooth.BLE_READ_REMOTE_RSSI";
+
+    public static final String EXTRA_COMMAND =
+            "com.android.cts.verifier.bluetooth.EXTRA_COMMAND";
+    public static final String EXTRA_WRITE_VALUE =
+            "com.android.cts.verifier.bluetooth.EXTRA_WRITE_VALUE";
+    public static final String EXTRA_BOOL =
+            "com.android.cts.verifier.bluetooth.EXTRA_BOOL";
+    public static final String EXTRA_CHARACTERISTIC_VALUE =
+            "com.android.cts.verifier.bluetooth.EXTRA_CHARACTERISTIC_VALUE";
+    public static final String EXTRA_DESCRIPTOR_VALUE =
+            "com.android.cts.verifier.bluetooth.EXTRA_DESCRIPTOR_VALUE";
+    public static final String EXTRA_RSSI_VALUE =
+            "com.android.cts.verifier.bluetooth.EXTRA_RSSI_VALUE";
+
+    private static final UUID SERVICE_UUID =
+            UUID.fromString("00009999-0000-1000-8000-00805f9b34fb");
+    private static final UUID CHARACTERISTIC_UUID =
+            UUID.fromString("00009998-0000-1000-8000-00805f9b34fb");
+    private static final UUID UPDATE_CHARACTERISTIC_UUID =
+            UUID.fromString("00009997-0000-1000-8000-00805f9b34fb");
+    private static final UUID DESCRIPTOR_UUID =
+            UUID.fromString("00009996-0000-1000-8000-00805f9b34fb");
+
+    private BluetoothManager mBluetoothManager;
+    private BluetoothAdapter mBluetoothAdapter;
+    private BluetoothDevice mDevice;
+    private BluetoothGatt mBluetoothGatt;
+    private Handler mHandler;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+        mBluetoothAdapter = mBluetoothManager.getAdapter();
+        mHandler = new Handler();
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (intent != null) handleIntent(intent);
+        return START_NOT_STICKY;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mBluetoothGatt.disconnect();
+        mBluetoothGatt.close();
+    }
+
+    private void handleIntent(Intent intent) {
+        int command = intent.getIntExtra(EXTRA_COMMAND, -1);
+        String address = intent.getStringExtra(BluetoothDevice.EXTRA_DEVICE); // sometimes null
+        String writeValue = intent.getStringExtra(EXTRA_WRITE_VALUE); // sometimes null
+        boolean enable = intent.getBooleanExtra(EXTRA_BOOL, false);
+        BluetoothGattService service;
+        BluetoothGattCharacteristic characteristic;
+        BluetoothGattDescriptor descriptor;
+
+        switch (command) {
+            case COMMAND_CONNECT:
+                mDevice = mBluetoothAdapter.getRemoteDevice(address);
+                mBluetoothGatt = mDevice.connectGatt(this, false, mGattCallbacks);
+                break;
+            case COMMAND_DISCONNECT:
+                if (mBluetoothGatt != null) mBluetoothGatt.disconnect();
+                break;
+            case COMMAND_DISCOVER_SERVICE:
+                if (mBluetoothGatt != null) mBluetoothGatt.discoverServices();
+                break;
+            case COMMAND_READ_RSSI:
+                if (mBluetoothGatt != null) mBluetoothGatt.readRemoteRssi();
+                break;
+            case COMMAND_WRITE_CHARACTERISTIC:
+                writeCharacteristic(writeValue);
+                break;
+            case COMMAND_READ_CHARACTERISTIC:
+                readCharacteristic();
+                break;
+            case COMMAND_WRITE_DESCRIPTOR:
+                writeDescriptor(writeValue);
+                break;
+            case COMMAND_READ_DESCRIPTOR:
+                readDescriptor();
+                break;
+            case COMMAND_SET_NOTIFICATION:
+                setNotification(enable);
+                break;
+            case COMMAND_BEGIN_WRITE:
+                if (mBluetoothGatt != null) mBluetoothGatt.beginReliableWrite();
+                break;
+            case COMMAND_EXECUTE_WRITE:
+                if (mBluetoothGatt != null) mBluetoothGatt.executeReliableWrite();
+                break;
+            case COMMAND_ABORT_RELIABLE:
+                if (mBluetoothGatt != null) mBluetoothGatt.abortReliableWrite(mDevice);
+                break;
+            default:
+                showMessage("Unrecognized command: " + command);
+                break;
+        }
+    }
+
+    private void writeCharacteristic(String writeValue) {
+        BluetoothGattCharacteristic characteristic = getCharacteristic(CHARACTERISTIC_UUID);
+        if (characteristic == null) return;
+        characteristic.setValue(writeValue);
+        mBluetoothGatt.writeCharacteristic(characteristic);
+    }
+
+    private void readCharacteristic() {
+        BluetoothGattCharacteristic characteristic = getCharacteristic(CHARACTERISTIC_UUID);
+        if (characteristic != null) mBluetoothGatt.readCharacteristic(characteristic);
+    }
+
+    private void writeDescriptor(String writeValue) {
+        BluetoothGattDescriptor descriptor = getDescriptor();
+        if (descriptor == null) return;
+        descriptor.setValue(writeValue.getBytes());
+        mBluetoothGatt.writeDescriptor(descriptor);
+    }
+
+    private void readDescriptor() {
+        BluetoothGattDescriptor descriptor = getDescriptor();
+        if (descriptor != null) mBluetoothGatt.readDescriptor(descriptor);
+    }
+
+    private void setNotification(boolean enable) {
+        BluetoothGattCharacteristic characteristic = getCharacteristic(UPDATE_CHARACTERISTIC_UUID);
+        if (characteristic != null)
+            mBluetoothGatt.setCharacteristicNotification(characteristic, enable);
+    }
+
+    private void notifyConnected() {
+        Intent intent = new Intent(BLE_BLUETOOTH_CONNECTED);
+        sendBroadcast(intent);
+    }
+
+    private void notifyDisconnected() {
+        Intent intent = new Intent(BLE_BLUETOOTH_DISCONNECTED);
+        sendBroadcast(intent);
+    }
+
+    private void notifyServicesDiscovered() {
+        Intent intent = new Intent(BLE_SERVICES_DISCOVERED);
+        sendBroadcast(intent);
+    }
+
+    private void notifyCharacteristicRead(String value) {
+        Intent intent = new Intent(BLE_CHARACTERISTIC_READ);
+        intent.putExtra(EXTRA_CHARACTERISTIC_VALUE, value);
+        sendBroadcast(intent);
+    }
+
+    private void notifyCharacteristicWrite() {
+        Intent intent = new Intent(BLE_CHARACTERISTIC_WRITE);
+        sendBroadcast(intent);
+    }
+
+    private void notifyCharacteristicChanged(String value) {
+        Intent intent = new Intent(BLE_CHARACTERISTIC_CHANGED);
+        intent.putExtra(EXTRA_CHARACTERISTIC_VALUE, value);
+        sendBroadcast(intent);
+    }
+
+    private void notifyDescriptorRead(String value) {
+        Intent intent = new Intent(BLE_DESCRIPTOR_READ);
+        intent.putExtra(EXTRA_DESCRIPTOR_VALUE, value);
+        sendBroadcast(intent);
+    }
+
+    private void notifyDescriptorWrite() {
+        Intent intent = new Intent(BLE_DESCRIPTOR_WRITE);
+        sendBroadcast(intent);
+    }
+
+    private void notifyReliableWriteCompleted() {
+        Intent intent = new Intent(BLE_RELIABLE_WRITE_COMPLETED);
+        sendBroadcast(intent);
+    }
+
+    private void notifyReadRemoteRssi(int rssi) {
+        Intent intent = new Intent(BLE_READ_REMOTE_RSSI);
+        intent.putExtra(EXTRA_RSSI_VALUE, rssi);
+        sendBroadcast(intent);
+    }
+
+    private BluetoothGattService getService() {
+        if (mBluetoothGatt == null) return null;
+
+        BluetoothGattService service = mBluetoothGatt.getService(SERVICE_UUID);
+        if (service == null) {
+            showMessage("Service not found");
+            return null;
+        }
+        return service;
+    }
+
+    private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
+        BluetoothGattService service = getService();
+        if (service == null) return null;
+
+        BluetoothGattCharacteristic characteristic = service.getCharacteristic(uuid);
+        if (characteristic == null) {
+            showMessage("Characteristic not found");
+            return null;
+        }
+        return characteristic;
+    }
+
+    private BluetoothGattDescriptor getDescriptor() {
+        BluetoothGattCharacteristic characteristic = getCharacteristic(CHARACTERISTIC_UUID);
+        if (characteristic == null) return null;
+
+        BluetoothGattDescriptor descriptor = characteristic.getDescriptor(DESCRIPTOR_UUID);
+        if (descriptor == null) {
+            showMessage("Descriptor not found");
+            return null;
+        }
+        return descriptor;
+    }
+
+    private void showMessage(final String msg) {
+        mHandler.post(new Runnable() {
+            public void run() {
+                Toast.makeText(BleClientService.this, msg, Toast.LENGTH_SHORT).show();
+            }
+        });
+    }
+
+    private final BluetoothGattCallback mGattCallbacks = new BluetoothGattCallback() {
+        @Override
+        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+            if (DEBUG) Log.d(TAG, "onConnectionStateChange");
+            if (status == BluetoothGatt.GATT_SUCCESS) {
+                if (newState == BluetoothProfile.STATE_CONNECTED) notifyConnected();
+                else if (status == BluetoothProfile.STATE_DISCONNECTED) {
+                    notifyDisconnected();
+                    showMessage("Bluetooth LE disconnected");
+                }
+            }
+        }
+
+        @Override
+        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+            if ((status == BluetoothGatt.GATT_SUCCESS) &&
+                (mBluetoothGatt.getService(SERVICE_UUID) != null)) {
+                notifyServicesDiscovered();
+            }
+        }
+
+        @Override
+        public void onCharacteristicRead(BluetoothGatt gatt,
+                                         BluetoothGattCharacteristic characteristic, int status) {
+            if ((status == BluetoothGatt.GATT_SUCCESS) &&
+                (characteristic.getUuid().equals(CHARACTERISTIC_UUID))) {
+                notifyCharacteristicRead(characteristic.getStringValue(0));
+            }
+        }
+
+        @Override
+        public void onCharacteristicWrite(BluetoothGatt gatt,
+                                          BluetoothGattCharacteristic characteristic, int status) {
+            if (DEBUG) Log.d(TAG, "onCharacteristicWrite: characteristic.val=" + characteristic.getStringValue(0)
+                                  + " status=" + status);
+            BluetoothGattCharacteristic mCharacteristic = getCharacteristic(CHARACTERISTIC_UUID);
+            if ((status == BluetoothGatt.GATT_SUCCESS) &&
+                (characteristic.getStringValue(0).equals(mCharacteristic.getStringValue(0)))) {
+                notifyCharacteristicWrite();
+            }
+        }
+
+        @Override
+        public void onCharacteristicChanged(BluetoothGatt gatt,
+                                            BluetoothGattCharacteristic characteristic) {
+            if (characteristic.getUuid().equals(UPDATE_CHARACTERISTIC_UUID))
+                notifyCharacteristicChanged(characteristic.getStringValue(0));
+        }
+
+        @Override
+        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
+                                     int status) {
+            if ((status == BluetoothGatt.GATT_SUCCESS) &&
+                (descriptor.getUuid().equals(DESCRIPTOR_UUID))) {
+                notifyDescriptorRead(new String(descriptor.getValue()));
+            }
+        }
+
+        @Override
+        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
+                                      int status) {
+            if ((status == BluetoothGatt.GATT_SUCCESS) &&
+                (descriptor.getUuid().equals(DESCRIPTOR_UUID))) {
+                notifyDescriptorWrite();
+            }
+        }
+
+        @Override
+        public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
+            if (status == BluetoothGatt.GATT_SUCCESS) notifyReliableWriteCompleted();
+        }
+
+        @Override
+        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
+            if (status == BluetoothGatt.GATT_SUCCESS) notifyReadRemoteRssi(rssi);
+        }
+    };
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientTestActivity.java
new file mode 100644
index 0000000..a13d934
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientTestActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import com.android.cts.verifier.ManifestTestListAdapter;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.os.Bundle;
+
+public class BleClientTestActivity extends PassFailButtons.TestListActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_list);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.ble_client_test_name, R.string.ble_client_test_info, -1);
+
+        setTestListAdapter(new ManifestTestListAdapter(this, getClass().getName()));
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleDiscoverServiceActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleDiscoverServiceActivity.java
new file mode 100644
index 0000000..6896b04
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleDiscoverServiceActivity.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+public class BleDiscoverServiceActivity extends BleButtonActivity {
+    public BleDiscoverServiceActivity() {
+        super(BleButtonActivity.DISCOVER_SERVICE);
+    }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleNotifyCharacteristicActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleNotifyCharacteristicActivity.java
new file mode 100644
index 0000000..e0c79bf
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleNotifyCharacteristicActivity.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class BleNotifyCharacteristicActivity extends PassFailButtons.Activity {
+
+    private boolean mEnable;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ble_notify_characteristic);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.ble_notify_characteristic_name,
+                         R.string.ble_notify_characteristic_info, -1);
+
+        mEnable = false;
+
+        ((Button) findViewById(R.id.ble_notify)).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mEnable = !mEnable;
+                if (mEnable) ((Button) v).setText(getString(R.string.ble_stop_notification));
+                else ((Button) v).setText(getString(R.string.ble_begin_notification));
+
+                Intent intent = new Intent(BleNotifyCharacteristicActivity.this,
+                                           BleClientService.class);
+                intent.putExtra(BleClientService.EXTRA_COMMAND,
+                                BleClientService.COMMAND_SET_NOTIFICATION);
+                intent.putExtra(BleClientService.EXTRA_BOOL, mEnable);
+                startService(intent);
+            }
+        });
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BleClientService.BLE_CHARACTERISTIC_CHANGED);
+        registerReceiver(onBroadcast, filter);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        unregisterReceiver(onBroadcast);
+        mEnable = false;
+        Intent intent = new Intent(BleNotifyCharacteristicActivity.this,
+                                   BleClientService.class);
+        intent.putExtra(BleClientService.EXTRA_COMMAND,
+                        BleClientService.COMMAND_SET_NOTIFICATION);
+        intent.putExtra(BleClientService.EXTRA_BOOL, mEnable);
+        startService(intent);
+    }
+
+    private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String value = intent.getStringExtra(BleClientService.EXTRA_CHARACTERISTIC_VALUE);
+            ((TextView) findViewById(R.id.ble_notify_text)).setText(value);
+        }
+    };
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReadRssiActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReadRssiActivity.java
new file mode 100644
index 0000000..800499c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReadRssiActivity.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class BleReadRssiActivity extends PassFailButtons.Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ble_read_rssi);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.ble_read_rssi_name,
+                         R.string.ble_read_rssi_info, -1);
+
+        ((Button) findViewById(R.id.ble_read_rssi)).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent intent = new Intent(BleReadRssiActivity.this, BleClientService.class);
+                intent.putExtra(BleClientService.EXTRA_COMMAND,
+                                BleClientService.COMMAND_READ_RSSI);
+                startService(intent);
+            }
+        });
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BleClientService.BLE_READ_REMOTE_RSSI);
+        registerReceiver(onBroadcast, filter);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        unregisterReceiver(onBroadcast);
+    }
+
+    private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            int rssi = intent.getIntExtra(BleClientService.EXTRA_RSSI_VALUE, 128);
+            ((TextView) findViewById(R.id.ble_rssi_text)).setText(Integer.toString(rssi));
+        }
+    };
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReadWriteActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReadWriteActivity.java
new file mode 100644
index 0000000..22233ef
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReadWriteActivity.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+class BleReadWriteActivity extends PassFailButtons.Activity {
+
+    static final int CHARACTERISTIC = 0;
+    static final int DESCRIPTOR = 1;
+
+    private int mWriteCommand;
+    private int mReadCommand;
+    private String mWriteFilter;
+    private String mReadFilter;
+    private String mExtraValue;
+    private int mName;
+    private EditText mEditText;
+
+    BleReadWriteActivity(int target) {
+        if (target == CHARACTERISTIC) {
+            mWriteCommand = BleClientService.COMMAND_WRITE_CHARACTERISTIC;
+            mReadCommand = BleClientService.COMMAND_READ_CHARACTERISTIC;
+            mWriteFilter = BleClientService.BLE_CHARACTERISTIC_WRITE;
+            mReadFilter = BleClientService.BLE_CHARACTERISTIC_READ;
+            mExtraValue = BleClientService.EXTRA_CHARACTERISTIC_VALUE;
+            mName = R.string.ble_client_characteristic_name;
+        } else if (target == DESCRIPTOR) {
+            mWriteCommand = BleClientService.COMMAND_WRITE_DESCRIPTOR;
+            mReadCommand = BleClientService.COMMAND_READ_DESCRIPTOR;
+            mWriteFilter = BleClientService.BLE_DESCRIPTOR_WRITE;
+            mReadFilter = BleClientService.BLE_DESCRIPTOR_READ;
+            mExtraValue = BleClientService.EXTRA_DESCRIPTOR_VALUE;
+            mName = R.string.ble_client_descriptor_name;
+        }
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ble_client_read_write);
+        setPassFailButtonClickListeners();
+        setInfoResources(mName, R.string.ble_read_write_info, -1);
+
+        mEditText = (EditText) findViewById(R.id.write_text);
+
+        ((Button) findViewById(R.id.ble_write)).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                String writeValue = mEditText.getText().toString();
+                Intent intent = new Intent(BleReadWriteActivity.this, BleClientService.class);
+                intent.putExtra(BleClientService.EXTRA_COMMAND, mWriteCommand);
+                intent.putExtra(BleClientService.EXTRA_WRITE_VALUE, writeValue);
+                startService(intent);
+                mEditText.setText("");
+            }
+        });
+
+        ((Button) findViewById(R.id.ble_read)).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent intent = new Intent(BleReadWriteActivity.this, BleClientService.class);
+                intent.putExtra(BleClientService.EXTRA_COMMAND, mReadCommand);
+                startService(intent);
+            }
+        });
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(mReadFilter);
+        filter.addAction(mWriteFilter);
+        registerReceiver(onBroadcast, filter);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        unregisterReceiver(onBroadcast);
+    }
+
+    private void showMessage(String msg) {
+        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
+    }
+
+    private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action == mWriteFilter)
+                showMessage("Write successful callback");
+            else if (action == mReadFilter) {
+                String value = intent.getStringExtra(mExtraValue);
+                ((TextView) findViewById(R.id.read_text)).setText(value);
+            }
+        }
+    };
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReliableWriteActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReliableWriteActivity.java
new file mode 100644
index 0000000..c7460b5
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReliableWriteActivity.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+
+public class BleReliableWriteActivity extends PassFailButtons.Activity {
+
+    EditText mEditText;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ble_reliable_write);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.ble_reliable_write_name, R.string.ble_reliable_write_info, -1);
+        getPassButton().setEnabled(false);
+        ((Button) findViewById(R.id.ble_execute)).setEnabled(false);
+
+        mEditText = (EditText) findViewById(R.id.write_text);
+
+        ((Button) findViewById(R.id.ble_begin)).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent intent = new Intent(BleReliableWriteActivity.this, BleClientService.class);
+                intent.putExtra(BleClientService.EXTRA_COMMAND,
+                                BleClientService.COMMAND_BEGIN_WRITE);
+                startService(intent);
+            }
+        });
+
+        ((Button) findViewById(R.id.ble_write)).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                String writeValue = mEditText.getText().toString();
+                Intent intent = new Intent(BleReliableWriteActivity.this, BleClientService.class);
+                intent.putExtra(BleClientService.EXTRA_COMMAND,
+                                BleClientService.COMMAND_WRITE_CHARACTERISTIC);
+                intent.putExtra(BleClientService.EXTRA_WRITE_VALUE, writeValue);
+                startService(intent);
+                mEditText.setText("");
+            }
+        });
+
+        ((Button) findViewById(R.id.ble_execute)).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent intent = new Intent(BleReliableWriteActivity.this, BleClientService.class);
+                intent.putExtra(BleClientService.EXTRA_COMMAND,
+                                BleClientService.COMMAND_EXECUTE_WRITE);
+                startService(intent);
+            }
+        });
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BleClientService.BLE_CHARACTERISTIC_WRITE);
+        filter.addAction(BleClientService.BLE_RELIABLE_WRITE_COMPLETED);
+        registerReceiver(onBroadcast, filter);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        unregisterReceiver(onBroadcast);
+        Intent intent = new Intent(this, BleClientService.class);
+        intent.putExtra(BleClientService.EXTRA_COMMAND, BleClientService.COMMAND_ABORT_RELIABLE);
+        startService(intent);
+    }
+
+    private void showMessage(String msg) {
+        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
+    }
+
+    private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action == BleClientService.BLE_CHARACTERISTIC_WRITE) {
+                showMessage("Write value verified.");
+                ((Button) findViewById(R.id.ble_execute)).setEnabled(true);
+            } else if (action == BleClientService.BLE_RELIABLE_WRITE_COMPLETED) {
+                showMessage("Reliable write completed.");
+                getPassButton().setEnabled(true);
+            }
+        }
+    };
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerService.java
new file mode 100644
index 0000000..a896d69
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerService.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.UUID;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.BluetoothGattServer;
+import android.bluetooth.BluetoothGattServerCallback;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+import android.widget.Toast;
+
+public class BleServerService extends Service {
+
+    public static final boolean DEBUG = true;
+    public static final String TAG = "BleServerService";
+
+    public static final int COMMAND_ADD_SERVICE = 0;
+    public static final int COMMAND_WRITE_CHARACTERISTIC = 1;
+    public static final int COMMAND_WRITE_DESCRIPTOR = 2;
+
+    public static final String BLE_SERVER_CONNECTED =
+            "com.android.cts.verifier.bluetooth.BLE_SERVER_CONNECTED";
+    public static final String BLE_SERVER_DISCONNECTED =
+            "com.android.cts.verifier.bluetooth.BLE_SERVER_DISCONNECTED";
+    public static final String BLE_SERVICE_ADDED =
+            "com.android.cts.verifier.bluetooth.BLE_SERVICE_ADDED";
+    public static final String BLE_CHARACTERISTIC_READ_REQUEST =
+            "com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_READ_REQUEST";
+    public static final String BLE_CHARACTERISTIC_WRITE_REQUEST =
+            "com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_WRITE_REQUEST";
+    public static final String BLE_DESCRIPTOR_READ_REQUEST =
+            "com.android.cts.verifier.bluetooth.BLE_DESCRIPTOR_READ_REQUEST";
+    public static final String BLE_DESCRIPTOR_WRITE_REQUEST =
+            "com.android.cts.verifier.bluetooth.BLE_DESCRIPTOR_WRITE_REQUEST";
+    public static final String BLE_EXECUTE_WRITE =
+            "com.android.cts.verifier.bluetooth.BLE_EXECUTE_WRITE";
+
+    private static final UUID SERVICE_UUID =
+            UUID.fromString("00009999-0000-1000-8000-00805f9b34fb");
+    private static final UUID CHARACTERISTIC_UUID =
+            UUID.fromString("00009998-0000-1000-8000-00805f9b34fb");
+    private static final UUID UPDATE_CHARACTERISTIC_UUID =
+            UUID.fromString("00009997-0000-1000-8000-00805f9b34fb");
+    private static final UUID DESCRIPTOR_UUID =
+            UUID.fromString("00009996-0000-1000-8000-00805f9b34fb");
+
+    private BluetoothManager mBluetoothManager;
+    private BluetoothGattServer mGattServer;
+    private BluetoothGattService mService;
+    private BluetoothDevice mDevice;
+    private Timer mNotificationTimer;
+    private Handler mHandler;
+    private String mReliableWriteValue;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+        mGattServer = mBluetoothManager.openGattServer(this, mCallbacks);
+        mService = createService();
+        mGattServer.addService(mService);
+        mDevice = null;
+        mReliableWriteValue = null;
+
+        mHandler = new Handler();
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        return START_NOT_STICKY;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mDevice != null) mGattServer.cancelConnection(mDevice);
+        mGattServer.close();
+    }
+
+    private void writeCharacteristic(String writeValue) {
+        BluetoothGattCharacteristic characteristic = getCharacteristic(CHARACTERISTIC_UUID);
+        if (characteristic != null) return;
+        characteristic.setValue(writeValue);
+    }
+
+    private void writeDescriptor(String writeValue) {
+        BluetoothGattDescriptor descriptor = getDescriptor();
+        if (descriptor == null) return;
+        descriptor.setValue(writeValue.getBytes());
+    }
+
+    private void notifyConnected() {
+        if (DEBUG) Log.d(TAG, "notifyConnected");
+        Intent intent = new Intent(BLE_SERVER_CONNECTED);
+        sendBroadcast(intent);
+    }
+
+    private void notifyDisconnected() {
+        if (DEBUG) Log.d(TAG, "notifyDisconnected");
+        Intent intent = new Intent(BLE_SERVER_DISCONNECTED);
+        sendBroadcast(intent);
+    }
+
+    private void notifyServiceAdded() {
+        if (DEBUG) Log.d(TAG, "notifyServiceAdded");
+        Intent intent = new Intent(BLE_SERVICE_ADDED);
+        sendBroadcast(intent);
+    }
+
+    private void notifyCharacteristicReadRequest() {
+        if (DEBUG) Log.d(TAG, "notifyCharacteristicReadRequest");
+        Intent intent = new Intent(BLE_CHARACTERISTIC_READ_REQUEST);
+        sendBroadcast(intent);
+    }
+
+    private void notifyCharacteristicWriteRequest() {
+        if (DEBUG) Log.d(TAG, "notifyCharacteristicWriteRequest");
+        Intent intent = new Intent(BLE_CHARACTERISTIC_WRITE_REQUEST);
+        sendBroadcast(intent);
+    }
+
+    private void notifyDescriptorReadRequest() {
+        if (DEBUG) Log.d(TAG, "notifyDescriptorReadRequest");
+        Intent intent = new Intent(BLE_DESCRIPTOR_READ_REQUEST);
+        sendBroadcast(intent);
+    }
+
+    private void notifyDescriptorWriteRequest() {
+        if (DEBUG) Log.d(TAG, "notifyDescriptorWriteRequest");
+        Intent intent = new Intent(BLE_DESCRIPTOR_WRITE_REQUEST);
+        sendBroadcast(intent);
+    }
+
+    private void notifyExecuteWrite() {
+        if (DEBUG) Log.d(TAG, "notifyExecuteWrite");
+        Intent intent = new Intent(BLE_EXECUTE_WRITE);
+        sendBroadcast(intent);
+    }
+
+    private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
+        BluetoothGattCharacteristic characteristic =
+                mService.getCharacteristic(uuid);
+        if (characteristic == null) {
+            showMessage("Characteristic not found");
+            return null;
+        }
+        return characteristic;
+    }
+
+    private BluetoothGattDescriptor getDescriptor() {
+        BluetoothGattCharacteristic characteristic = getCharacteristic(CHARACTERISTIC_UUID);
+        if (characteristic == null) return null;
+
+        BluetoothGattDescriptor descriptor = characteristic.getDescriptor(DESCRIPTOR_UUID);
+        if (descriptor == null) {
+            showMessage("Descriptor not found");
+            return null;
+        }
+        return descriptor;
+    }
+
+    private BluetoothGattService createService() {
+        BluetoothGattService service =
+                new BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
+        BluetoothGattCharacteristic characteristic =
+                new BluetoothGattCharacteristic(CHARACTERISTIC_UUID, 0x0A, 0x11);
+        BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(DESCRIPTOR_UUID, 0x11);
+        characteristic.addDescriptor(descriptor);
+        service.addCharacteristic(characteristic);
+
+        BluetoothGattCharacteristic notiCharacteristic =
+                new BluetoothGattCharacteristic(UPDATE_CHARACTERISTIC_UUID, 0x10, 0x00);
+        service.addCharacteristic(notiCharacteristic);
+
+        return service;
+    }
+
+    private void beginNotification() {
+        TimerTask task = new TimerTask() {
+            @Override
+            public void run() {
+                BluetoothGattCharacteristic characteristic =
+                        mService.getCharacteristic(UPDATE_CHARACTERISTIC_UUID);
+                if (characteristic == null) return;
+
+                String date = (new Date()).toString();
+                characteristic.setValue(date);
+                mGattServer.notifyCharacteristicChanged(mDevice, characteristic, false);
+            }
+        };
+        mNotificationTimer = new Timer();
+        mNotificationTimer.schedule(task, 0, 1000);
+    }
+
+    private void stopNotification() {
+        if (mNotificationTimer == null) return;
+        mNotificationTimer.cancel();
+        mNotificationTimer = null;
+    }
+
+    private void showMessage(final String msg) {
+        mHandler.post(new Runnable() {
+            public void run() {
+                Toast.makeText(BleServerService.this, msg, Toast.LENGTH_SHORT).show();
+            }
+        });
+    }
+
+    private final BluetoothGattServerCallback mCallbacks = new BluetoothGattServerCallback() {
+        @Override
+        public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
+            if (DEBUG) Log.d(TAG, "onConnectionStateChange: newState=" + newState);
+            if (status == BluetoothGatt.GATT_SUCCESS) {
+                if (newState == BluetoothProfile.STATE_CONNECTED) {
+                    mDevice = device;
+                    notifyConnected();
+                    beginNotification();
+                } else if (status == BluetoothProfile.STATE_DISCONNECTED) {
+                    stopNotification();
+                    notifyDisconnected();
+                    mDevice = null;
+                }
+            }
+        }
+
+        @Override
+        public void onServiceAdded(int status, BluetoothGattService service) {
+            if (DEBUG) Log.d(TAG, "onServiceAdded()");
+            if (status == BluetoothGatt.GATT_SUCCESS) notifyServiceAdded();
+        }
+
+        @Override
+        public void onCharacteristicReadRequest(BluetoothDevice device, int requestId,
+                            int offset, BluetoothGattCharacteristic characteristic) {
+            if (DEBUG) Log.d(TAG, "onCharacteristicReadRequest()");
+
+            notifyCharacteristicReadRequest();
+            mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0,
+                                     characteristic.getValue());
+        }
+
+        @Override
+        public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
+                                                 BluetoothGattCharacteristic characteristic,
+                                                 boolean preparedWrite, boolean responseNeeded,
+                                                 int offset, byte[] value) {
+            if (DEBUG) Log.d(TAG, "onCharacteristicWriteRequest: preparedWrite=" + preparedWrite);
+
+            notifyCharacteristicWriteRequest();
+            if (preparedWrite) mReliableWriteValue = new String(value);
+            else characteristic.setValue(value);
+
+            if (responseNeeded)
+                mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);
+        }
+
+        @Override
+        public void onDescriptorReadRequest(BluetoothDevice device, int requestId,
+                                            int offset, BluetoothGattDescriptor descriptor) {
+            if (DEBUG) Log.d(TAG, "onDescriptorReadRequest(): (descriptor == getDescriptor())="
+                                  + (descriptor == getDescriptor()));
+
+            notifyDescriptorReadRequest();
+            mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0,
+                                     descriptor.getValue());
+        }
+
+        @Override
+        public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
+                                             BluetoothGattDescriptor descriptor,
+                                             boolean preparedWrite, boolean responseNeeded,
+                                             int offset,  byte[] value) {
+            if (DEBUG) Log.d(TAG, "onDescriptorWriteRequest(): (descriptor == getDescriptor())="
+                                  + (descriptor == getDescriptor()));
+
+            notifyDescriptorWriteRequest();
+            descriptor.setValue(value);
+            if (responseNeeded)
+                mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);
+        }
+
+        @Override
+        public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
+            if (DEBUG) Log.d(TAG, "onExecuteWrite");
+            if (execute) {
+                notifyExecuteWrite();
+                getCharacteristic(CHARACTERISTIC_UUID).setValue(mReliableWriteValue);
+            }
+        }
+    };
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerStartActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerStartActivity.java
new file mode 100644
index 0000000..ec31fde
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerStartActivity.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+public class BleServerStartActivity extends PassFailButtons.Activity {
+
+    private List<Test> mTestList;
+    private TestAdapter mTestAdapter;
+    private int mAllPassed;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ble_server_start);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.ble_server_start_name,
+                         R.string.ble_server_start_info, -1);
+        getPassButton().setEnabled(false);
+
+        mTestList = setupTestList();
+        mTestAdapter = new TestAdapter(this, mTestList);
+
+        ListView listView = (ListView) findViewById(R.id.ble_server_tests);
+        listView.setAdapter(mTestAdapter);
+
+        mAllPassed = 0;
+        startService(new Intent(this, BleServerService.class));
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BleServerService.BLE_SERVICE_ADDED);
+        filter.addAction(BleServerService.BLE_SERVER_CONNECTED);
+        filter.addAction(BleServerService.BLE_CHARACTERISTIC_READ_REQUEST);
+        filter.addAction(BleServerService.BLE_CHARACTERISTIC_WRITE_REQUEST);
+        filter.addAction(BleServerService.BLE_DESCRIPTOR_READ_REQUEST);
+        filter.addAction(BleServerService.BLE_DESCRIPTOR_WRITE_REQUEST);
+        filter.addAction(BleServerService.BLE_EXECUTE_WRITE);
+        filter.addAction(BleServerService.BLE_SERVER_DISCONNECTED);
+        registerReceiver(onBroadcast, filter);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        unregisterReceiver(onBroadcast);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        stopService(new Intent(this, BleServerService.class));
+    }
+
+    private List<Test> setupTestList() {
+        ArrayList<Test> testList = new ArrayList<Test>();
+        testList.add(new Test(R.string.ble_server_add_service));
+        testList.add(new Test(R.string.ble_server_receiving_connect));
+        testList.add(new Test(R.string.ble_server_read_characteristic));
+        testList.add(new Test(R.string.ble_server_write_characteristic));
+        testList.add(new Test(R.string.ble_server_read_descriptor));
+        testList.add(new Test(R.string.ble_server_write_descriptor));
+        testList.add(new Test(R.string.ble_server_reliable_write));
+        testList.add(new Test(R.string.ble_server_receiving_disconnect));
+        return testList;
+    }
+
+    class Test {
+        private boolean passed;
+        private int instructions;
+
+        private Test(int instructions) {
+            passed = false;
+            this.instructions = instructions;
+        }
+    }
+
+    private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action == BleServerService.BLE_SERVICE_ADDED) {
+                mTestList.get(0).passed = true;
+                mAllPassed |= 0x01;
+            } else if (action == BleServerService.BLE_SERVER_CONNECTED) {
+                mTestList.get(1).passed = true;
+                mAllPassed |= 0x02;
+            } else if (action == BleServerService.BLE_CHARACTERISTIC_READ_REQUEST) {
+                mTestList.get(2).passed = true;
+                mAllPassed |= 0x04;
+            } else if (action == BleServerService.BLE_CHARACTERISTIC_WRITE_REQUEST) {
+                mTestList.get(3).passed = true;
+                mAllPassed |= 0x08;
+            } else if (action == BleServerService.BLE_DESCRIPTOR_READ_REQUEST) {
+                mTestList.get(4).passed = true;
+                mAllPassed |= 0x10;
+            } else if (action == BleServerService.BLE_DESCRIPTOR_WRITE_REQUEST) {
+                mTestList.get(5).passed = true;
+                mAllPassed |= 0x20;
+            } else if (action == BleServerService.BLE_EXECUTE_WRITE) {
+                mTestList.get(6).passed = true;
+                mAllPassed |= 0x40;
+            } else if (action == BleServerService.BLE_SERVER_DISCONNECTED) {
+                mTestList.get(7).passed = true;
+                mAllPassed |= 0x80;
+            }
+            mTestAdapter.notifyDataSetChanged();
+            if (mAllPassed == 0xFF) getPassButton().setEnabled(true);
+        }
+    };
+
+    class TestAdapter extends BaseAdapter {
+        Context context;
+        List<Test> tests;
+        LayoutInflater inflater;
+
+        public TestAdapter(Context context, List<Test> tests) {
+            this.context = context;
+            inflater = LayoutInflater.from(context);
+            this.tests = tests;
+        }
+
+        @Override
+        public int getCount() {
+            return tests.size();
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return tests.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            ViewGroup vg;
+
+            if (convertView != null) {
+                vg = (ViewGroup) convertView;
+            } else {
+                vg = (ViewGroup) inflater.inflate(R.layout.ble_server_start_item, null);
+            }
+
+            Test test = tests.get(position);
+            if (test.passed) {
+                ((ImageView) vg.findViewById(R.id.status)).setImageResource(R.drawable.fs_good);
+            } else {
+                ((ImageView) vg.findViewById(R.id.status)).
+                                setImageResource(R.drawable.fs_indeterminate);
+            }
+            ((TextView) vg.findViewById(R.id.instructions)).setText(test.instructions);
+
+            return vg;
+        }
+    }
+}
\ No newline at end of file