DO NOT MERGE: SDK sample code for Bluetooth HDP API.
Cherry pick from ics-mr1 Change ID I0f68ba42309c960f0df1e537b0932c6d68d9657b

Change-Id: I1d5e37fc24d82f8f9f846c82b93f68dcc58d95cc
diff --git a/build/sdk.atree b/build/sdk.atree
index c63623f..44430f6 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -159,6 +159,7 @@
 development/samples/BackupRestore              samples/${PLATFORM_NAME}/BackupRestore
 development/samples/BasicGLSurfaceView         samples/${PLATFORM_NAME}/BasicGLSurfaceView
 development/samples/BluetoothChat              samples/${PLATFORM_NAME}/BluetoothChat
+development/samples/BluetoothHDP               samples/${PLATFORM_NAME}/BluetoothHDP
 development/samples/ContactManager             samples/${PLATFORM_NAME}/ContactManager
 development/samples/CrossCompatibility         samples/${PLATFORM_NAME}/CrossCompatibility
 development/samples/CubeLiveWallpaper          samples/${PLATFORM_NAME}/CubeLiveWallpaper
diff --git a/samples/BluetoothHDP/Android.mk b/samples/BluetoothHDP/Android.mk
new file mode 100644
index 0000000..2fb0a11
--- /dev/null
+++ b/samples/BluetoothHDP/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := BluetoothHDP
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/BluetoothHDP/AndroidManifest.xml b/samples/BluetoothHDP/AndroidManifest.xml
new file mode 100644
index 0000000..21723d1
--- /dev/null
+++ b/samples/BluetoothHDP/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.example.bluetooth.health"
+      android:versionCode="1"
+      android:versionName="1.0">
+    <uses-sdk android:minSdkVersion="14"
+              android:targetSdkVersion="14" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+
+    <application android:icon="@drawable/icon" android:label="@string/app_name">
+        <activity android:name=".BluetoothHDPActivity"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <service android:name=".BluetoothHDPService" />
+    </application>
+</manifest>
diff --git a/samples/BluetoothHDP/_index.html b/samples/BluetoothHDP/_index.html
new file mode 100755
index 0000000..3871bf3
--- /dev/null
+++ b/samples/BluetoothHDP/_index.html
@@ -0,0 +1,6 @@
+<p>A sample application that demonstrates how to communicate with a Bluetooth Health Device Profile (HDP) device.  This feature is available on Android 4.0 (API level 14) or above platforms.</p>
+
+<p>The application manages connection with Bluetooth HDP-enabled devices.  Possible device type include blood pressure monitor, glucose meter, thermometer, etc.  Upon connection, the application retrieves raw data sent from the device.  Note that in order to interpret the data, a parser that conforms to the IEEE 11073-xxxxx specifications would be required.</p>
+
+<img alt="" src="../images/BluetoothHDP.png" />
+
diff --git a/samples/BluetoothHDP/res/drawable-hdpi/icon.png b/samples/BluetoothHDP/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..e43c502
--- /dev/null
+++ b/samples/BluetoothHDP/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/samples/BluetoothHDP/res/drawable-mdpi/icon.png b/samples/BluetoothHDP/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..8d9e9df
--- /dev/null
+++ b/samples/BluetoothHDP/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/samples/BluetoothHDP/res/drawable-xhdpi/icon.png b/samples/BluetoothHDP/res/drawable-xhdpi/icon.png
new file mode 100644
index 0000000..1aea335
--- /dev/null
+++ b/samples/BluetoothHDP/res/drawable-xhdpi/icon.png
Binary files differ
diff --git a/samples/BluetoothHDP/res/drawable-xhdpi/led_off.png b/samples/BluetoothHDP/res/drawable-xhdpi/led_off.png
new file mode 100644
index 0000000..884533c
--- /dev/null
+++ b/samples/BluetoothHDP/res/drawable-xhdpi/led_off.png
Binary files differ
diff --git a/samples/BluetoothHDP/res/drawable-xhdpi/led_on.png b/samples/BluetoothHDP/res/drawable-xhdpi/led_on.png
new file mode 100644
index 0000000..6542b00
--- /dev/null
+++ b/samples/BluetoothHDP/res/drawable-xhdpi/led_on.png
Binary files differ
diff --git a/samples/BluetoothHDP/res/drawable/led_indicator.xml b/samples/BluetoothHDP/res/drawable/led_indicator.xml
new file mode 100644
index 0000000..d6140c8
--- /dev/null
+++ b/samples/BluetoothHDP/res/drawable/led_indicator.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+-->
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:maxLevel="0" android:drawable="@drawable/led_off" />
+    <item android:maxLevel="1" android:drawable="@drawable/led_on" />
+</level-list>
diff --git a/samples/BluetoothHDP/res/layout/console.xml b/samples/BluetoothHDP/res/layout/console.xml
new file mode 100644
index 0000000..8d8a3fe
--- /dev/null
+++ b/samples/BluetoothHDP/res/layout/console.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+    <LinearLayout android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_margin="5dp">
+        <TextView android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/app_registration"
+            android:textSize="20sp"
+            android:textStyle="bold" />
+        <LinearLayout android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dp"
+            android:gravity="center" >
+            <Button android:id="@+id/button_register_app"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:minWidth="130dp"
+                android:text="@string/register" />
+            <Button android:id="@+id/button_unregister_app"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:minWidth="130dp"
+                android:text="@string/unregister" />
+        </LinearLayout>
+
+        <TextView android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/channel_connection"
+            android:textSize="20sp"
+            android:textStyle="bold" />
+        <TextView android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/channel_connection_desc"
+            android:textSize="14sp" />
+        <LinearLayout android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dp"
+            android:gravity="center" >
+            <Button android:id="@+id/button_connect_channel"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:minWidth="130dp"
+                android:text="@string/connect" />
+            <Button android:id="@+id/button_disconnect_channel"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:minWidth="130dp"
+                android:text="@string/disconnect" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dp"
+            android:orientation="horizontal" >
+            <TextView android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/connection_state"
+                android:textSize="20sp"
+                android:textStyle="bold" />
+            <Space android:layout_width="10dp"
+                android:layout_height="0px" />
+            <TextView android:id="@+id/connect_ind"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/disconnected"
+                android:textSize="18sp"/>
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dp"
+            android:orientation="horizontal" >
+            <TextView android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/data_ind"
+                android:textSize="20sp"
+                android:textStyle="bold" />
+            <Space android:layout_width="10dp"
+                android:layout_height="0px" />
+            <ImageView android:id="@+id/data_ind"
+                android:layout_width="20dp"
+                android:layout_height="20dp"
+                android:layout_marginTop="4dp"
+                android:src="@drawable/led_indicator" />
+        </LinearLayout>
+        <View android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:background="#FFFFFF"/>
+        <TextView android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dp"
+            android:text="@string/status_msg"
+            android:textSize="20sp"
+            android:textStyle="bold" />
+        <TextView android:id="@+id/status_msg"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/empty"
+            android:textSize="18sp"
+            android:layout_margin="10dp"/>
+    </LinearLayout>
+</ScrollView>
diff --git a/samples/BluetoothHDP/res/values/strings.xml b/samples/BluetoothHDP/res/values/strings.xml
new file mode 100644
index 0000000..21e3a84
--- /dev/null
+++ b/samples/BluetoothHDP/res/values/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+-->
+<resources>
+    <string name="app_name">HDP Sample</string>
+    <string name="app_registration">App Registration</string>
+    <string name="bluetooth_not_available">Bluetooth is not available</string>
+    <string name="bluetooth_health_profile_not_available">Bluetooth health profile is not available</string>
+    <string name="channel_connection">Channel Connection</string>
+    <string name="channel_connection_desc">(some devices automatically initiate connection)</string>
+    <string name="connect">Connect</string>
+    <string name="connected">CONNECTED</string>
+    <string name="connection_state">Connection State</string>
+    <string name="data_ind">Data Indicator</string>
+    <string name="disconnect">Disconnect</string>
+    <string name="disconnected">DISCONNECTED</string>
+    <string name="empty"> </string>
+    <string name="none">None...</string>
+    <string name="ok">Okay</string>
+    <string name="read_data">Reading data...</string>
+    <string name="read_data_done">Done with reading data...</string>
+    <string name="register">Register</string>
+    <string name="select_device">Select a device</string>
+    <string name="status_create_channel">Create channel status: %d</string>
+    <string name="status_destroy_channel">Destroy channel status: %d</string>
+    <string name="status_msg">Status Message</string>
+    <string name="status_reg">App registration status: %d</string>
+    <string name="status_unreg">App unregistration status: %d</string>
+    <string name="unregister">Unregister</string>
+</resources>
diff --git a/samples/BluetoothHDP/src/com/example/bluetooth/health/BluetoothHDPActivity.java b/samples/BluetoothHDP/src/com/example/bluetooth/health/BluetoothHDPActivity.java
new file mode 100644
index 0000000..ca8b182
--- /dev/null
+++ b/samples/BluetoothHDP/src/com/example/bluetooth/health/BluetoothHDPActivity.java
@@ -0,0 +1,373 @@
+/*
+ * 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.example.bluetooth.health;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Main user interface for the Sample application.  All Bluetooth health-related
+ * operations happen in {@link BluetoothHDPService}.  This activity passes messages to and from
+ * the service.
+ */
+public class BluetoothHDPActivity extends Activity {
+    private static final String TAG = "BluetoothHealthActivity";
+
+    // Use the appropriate IEEE 11073 data types based on the devices used.
+    // Below are some examples.  Refer to relevant Bluetooth HDP specifications for detail.
+    //     0x1007 - blood pressure meter
+    //     0x1008 - body thermometer
+    //     0x100F - body weight scale
+    private static final int HEALTH_PROFILE_SOURCE_DATA_TYPE = 0x1007;
+
+    private static final int REQUEST_ENABLE_BT = 1;
+
+    private TextView mConnectIndicator;
+    private ImageView mDataIndicator;
+    private TextView mStatusMessage;
+
+    private BluetoothAdapter mBluetoothAdapter;
+    private BluetoothDevice[] mAllBondedDevices;
+    private BluetoothDevice mDevice;
+    private int mDeviceIndex = 0;
+    private Resources mRes;
+    private Messenger mHealthService;
+    private boolean mHealthServiceBound;
+
+    // Handles events sent by {@link HealthHDPService}.
+    private Handler mIncomingHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                // Application registration complete.
+                case BluetoothHDPService.STATUS_HEALTH_APP_REG:
+                    mStatusMessage.setText(
+                            String.format(mRes.getString(R.string.status_reg),
+                            msg.arg1));
+                    break;
+                // Application unregistration complete.
+                case BluetoothHDPService.STATUS_HEALTH_APP_UNREG:
+                    mStatusMessage.setText(
+                            String.format(mRes.getString(R.string.status_unreg),
+                            msg.arg1));
+                    break;
+                // Reading data from HDP device.
+                case BluetoothHDPService.STATUS_READ_DATA:
+                    mStatusMessage.setText(mRes.getString(R.string.read_data));
+                    mDataIndicator.setImageLevel(1);
+                    break;
+                // Finish reading data from HDP device.
+                case BluetoothHDPService.STATUS_READ_DATA_DONE:
+                    mStatusMessage.setText(mRes.getString(R.string.read_data_done));
+                    mDataIndicator.setImageLevel(0);
+                    break;
+                // Channel creation complete.  Some devices will automatically establish
+                // connection.
+                case BluetoothHDPService.STATUS_CREATE_CHANNEL:
+                    mStatusMessage.setText(
+                            String.format(mRes.getString(R.string.status_create_channel),
+                            msg.arg1));
+                    mConnectIndicator.setText(R.string.connected);
+                    break;
+                // Channel destroy complete.  This happens when either the device disconnects or
+                // there is extended inactivity.
+                case BluetoothHDPService.STATUS_DESTROY_CHANNEL:
+                    mStatusMessage.setText(
+                            String.format(mRes.getString(R.string.status_destroy_channel),
+                            msg.arg1));
+                    mConnectIndicator.setText(R.string.disconnected);
+                    break;
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+    };
+
+    private final Messenger mMessenger = new Messenger(mIncomingHandler);
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Check for Bluetooth availability on the Android platform.
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (mBluetoothAdapter == null) {
+            Toast.makeText(this, R.string.bluetooth_not_available, Toast.LENGTH_LONG);
+            finish();
+            return;
+        }
+        setContentView(R.layout.console);
+        mConnectIndicator = (TextView) findViewById(R.id.connect_ind);
+        mStatusMessage = (TextView) findViewById(R.id.status_msg);
+        mDataIndicator = (ImageView) findViewById(R.id.data_ind);
+        mRes = getResources();
+        mHealthServiceBound = false;
+
+        // Initiates application registration through {@link BluetoothHDPService}.
+        Button registerAppButton = (Button) findViewById(R.id.button_register_app);
+        registerAppButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                sendMessage(BluetoothHDPService.MSG_REG_HEALTH_APP,
+                        HEALTH_PROFILE_SOURCE_DATA_TYPE);
+            }
+        });
+
+        // Initiates application unregistration through {@link BluetoothHDPService}.
+        Button unregisterAppButton = (Button) findViewById(R.id.button_unregister_app);
+        unregisterAppButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                sendMessage(BluetoothHDPService.MSG_UNREG_HEALTH_APP, 0);
+            }
+        });
+
+        // Initiates channel creation through {@link BluetoothHDPService}.  Some devices will
+        // initiate the channel connection, in which case, it is not necessary to do this in the
+        // application.  When pressed, the user is asked to select from one of the bonded devices
+        // to connect to.
+        Button connectButton = (Button) findViewById(R.id.button_connect_channel);
+        connectButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                mAllBondedDevices =
+                        (BluetoothDevice[]) mBluetoothAdapter.getBondedDevices().toArray(
+                                new BluetoothDevice[0]);
+
+                if (mAllBondedDevices.length > 0) {
+                    int deviceCount = mAllBondedDevices.length;
+                    if (mDeviceIndex < deviceCount) mDevice = mAllBondedDevices[mDeviceIndex];
+                    else {
+                        mDeviceIndex = 0;
+                        mDevice = mAllBondedDevices[0];
+                    }
+                    String[] deviceNames = new String[deviceCount];
+                    int i = 0;
+                    for (BluetoothDevice device : mAllBondedDevices) {
+                        deviceNames[i++] = device.getName();
+                    }
+                    SelectDeviceDialogFragment deviceDialog =
+                            SelectDeviceDialogFragment.newInstance(deviceNames, mDeviceIndex);
+                    deviceDialog.show(getFragmentManager(), "deviceDialog");
+                }
+            }
+        });
+
+        // Initiates channel disconnect through {@link BluetoothHDPService}.
+        Button disconnectButton = (Button) findViewById(R.id.button_disconnect_channel);
+        disconnectButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                disconnectChannel();
+            }
+        });
+        registerReceiver(mReceiver, initIntentFilter());
+    }
+
+    // Sets up communication with {@link BluetoothHDPService}.
+    private ServiceConnection mConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mHealthServiceBound = true;
+            Message msg = Message.obtain(null, BluetoothHDPService.MSG_REG_CLIENT);
+            msg.replyTo = mMessenger;
+            mHealthService = new Messenger(service);
+            try {
+                mHealthService.send(msg);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Unable to register client to service.");
+                e.printStackTrace();
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            mHealthService = null;
+            mHealthServiceBound = false;
+        }
+    };
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        if (mHealthServiceBound) unbindService(mConnection);
+        unregisterReceiver(mReceiver);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        // If Bluetooth is not on, request that it be enabled.
+        if (!mBluetoothAdapter.isEnabled()) {
+            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+            startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
+        } else {
+            initialize();
+        }
+    }
+
+    /**
+     * Ensures user has turned on Bluetooth on the Android device.
+     */
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        switch (requestCode) {
+        case REQUEST_ENABLE_BT:
+            if (resultCode == Activity.RESULT_OK) {
+                initialize();
+            } else {
+                finish();
+                return;
+            }
+        }
+    }
+
+    /**
+     * Used by {@link SelectDeviceDialogFragment} to record the bonded Bluetooth device selected
+     * by the user.
+     *
+     * @param position Position of the bonded Bluetooth device in the array.
+     */
+    public void setDevice(int position) {
+        mDevice = this.mAllBondedDevices[position];
+        mDeviceIndex = position;
+    }
+
+    private void connectChannel() {
+        sendMessageWithDevice(BluetoothHDPService.MSG_CONNECT_CHANNEL);
+    }
+
+    private void disconnectChannel() {
+        sendMessageWithDevice(BluetoothHDPService.MSG_DISCONNECT_CHANNEL);
+    }
+
+    private void initialize() {
+        // Starts health service.
+        Intent intent = new Intent(this, BluetoothHDPService.class);
+        startService(intent);
+        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+    }
+
+    // Intent filter and broadcast receive to handle Bluetooth on event.
+    private IntentFilter initIntentFilter() {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        return filter;
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
+                if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR) ==
+                    BluetoothAdapter.STATE_ON) {
+                    initialize();
+                }
+            }
+        }
+    };
+
+    // Sends a message to {@link BluetoothHDPService}.
+    private void sendMessage(int what, int value) {
+        if (mHealthService == null) {
+            Log.d(TAG, "Health Service not connected.");
+            return;
+        }
+
+        try {
+            mHealthService.send(Message.obtain(null, what, value, 0));
+        } catch (RemoteException e) {
+            Log.w(TAG, "Unable to reach service.");
+            e.printStackTrace();
+        }
+    }
+
+    // Sends an update message, along with an HDP BluetoothDevice object, to
+    // {@link BluetoothHDPService}.  The BluetoothDevice object is needed by the channel creation
+    // method.
+    private void sendMessageWithDevice(int what) {
+        if (mHealthService == null) {
+            Log.d(TAG, "Health Service not connected.");
+            return;
+        }
+
+        try {
+            mHealthService.send(Message.obtain(null, what, mDevice));
+        } catch (RemoteException e) {
+            Log.w(TAG, "Unable to reach service.");
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Dialog to display a list of bonded Bluetooth devices for user to select from.  This is
+     * needed only for channel connection initiated from the application.
+     */
+    public static class SelectDeviceDialogFragment extends DialogFragment {
+
+        public static SelectDeviceDialogFragment newInstance(String[] names, int position) {
+            SelectDeviceDialogFragment frag = new SelectDeviceDialogFragment();
+            Bundle args = new Bundle();
+            args.putStringArray("names", names);
+            args.putInt("position", position);
+            frag.setArguments(args);
+            return frag;
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            String[] deviceNames = getArguments().getStringArray("names");
+            int position = getArguments().getInt("position", -1);
+            if (position == -1) position = 0;
+            return new AlertDialog.Builder(getActivity())
+                    .setTitle(R.string.select_device)
+                    .setPositiveButton(R.string.ok,
+                        new DialogInterface.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int which) {
+                                ((BluetoothHDPActivity) getActivity()).connectChannel();
+                            }
+                        })
+                    .setSingleChoiceItems(deviceNames, position,
+                        new DialogInterface.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int which) {
+                                ((BluetoothHDPActivity) getActivity()).setDevice(which);
+                            }
+                        }
+                    )
+                    .create();
+        }
+    }
+}
diff --git a/samples/BluetoothHDP/src/com/example/bluetooth/health/BluetoothHDPService.java b/samples/BluetoothHDP/src/com/example/bluetooth/health/BluetoothHDPService.java
new file mode 100644
index 0000000..f090aa7
--- /dev/null
+++ b/samples/BluetoothHDP/src/com/example/bluetooth/health/BluetoothHDPService.java
@@ -0,0 +1,311 @@
+/*
+ * 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.example.bluetooth.health;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHealth;
+import android.bluetooth.BluetoothHealthAppConfiguration;
+import android.bluetooth.BluetoothHealthCallback;
+import android.bluetooth.BluetoothProfile;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * This Service encapsulates Bluetooth Health API to establish, manage, and disconnect
+ * communication between the Android device and a Bluetooth HDP-enabled device.  Possible HDP
+ * device type includes blood pressure monitor, glucose meter, thermometer, etc.
+ *
+ * As outlined in the
+ * <a href="http://developer.android.com/reference/android/bluetooth/BluetoothHealth.html">BluetoothHealth</a>
+ * documentation, the steps involve:
+ * 1. Get a reference to the BluetoothHealth proxy object.
+ * 2. Create a BluetoothHealth callback and register an application configuration that acts as a
+ *    Health SINK.
+ * 3. Establish connection to a health device.  Some devices will initiate the connection.  It is
+ *    unnecessary to carry out this step for those devices.
+ * 4. When connected successfully, read / write to the health device using the file descriptor.
+ *    The received data needs to be interpreted using a health manager which implements the
+ *    IEEE 11073-xxxxx specifications.
+ * 5. When done, close the health channel and unregister the application.  The channel will
+ *    also close when there is extended inactivity.
+ */
+public class BluetoothHDPService extends Service {
+    private static final String TAG = "BluetoothHDPService";
+
+    public static final int RESULT_OK = 0;
+    public static final int RESULT_FAIL = -1;
+
+    // Status codes sent back to the UI client.
+    // Application registration complete.
+    public static final int STATUS_HEALTH_APP_REG = 100;
+    // Application unregistration complete.
+    public static final int STATUS_HEALTH_APP_UNREG = 101;
+    // Channel creation complete.
+    public static final int STATUS_CREATE_CHANNEL = 102;
+    // Channel destroy complete.
+    public static final int STATUS_DESTROY_CHANNEL = 103;
+    // Reading data from Bluetooth HDP device.
+    public static final int STATUS_READ_DATA = 104;
+    // Done with reading data.
+    public static final int STATUS_READ_DATA_DONE = 105;
+
+    // Message codes received from the UI client.
+    // Register client with this service.
+    public static final int MSG_REG_CLIENT = 200;
+    // Unregister client from this service.
+    public static final int MSG_UNREG_CLIENT = 201;
+    // Register health application.
+    public static final int MSG_REG_HEALTH_APP = 300;
+    // Unregister health application.
+    public static final int MSG_UNREG_HEALTH_APP = 301;
+    // Connect channel.
+    public static final int MSG_CONNECT_CHANNEL = 400;
+    // Disconnect channel.
+    public static final int MSG_DISCONNECT_CHANNEL = 401;
+
+    private BluetoothHealthAppConfiguration mHealthAppConfig;
+    private BluetoothAdapter mBluetoothAdapter;
+    private BluetoothHealth mBluetoothHealth;
+    private BluetoothDevice mDevice;
+    private int mChannelId;
+
+    private Messenger mClient;
+
+    // Handles events sent by {@link HealthHDPActivity}.
+    private class IncomingHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                // Register UI client to this service so the client can receive messages.
+                case MSG_REG_CLIENT:
+                    Log.d(TAG, "Activity client registered");
+                    mClient = msg.replyTo;
+                    break;
+                // Unregister UI client from this service.
+                case MSG_UNREG_CLIENT:
+                    mClient = null;
+                    break;
+                // Register health application.
+                case MSG_REG_HEALTH_APP:
+                    registerApp(msg.arg1);
+                    break;
+                // Unregister health application.
+                case MSG_UNREG_HEALTH_APP:
+                    unregisterApp();
+                    break;
+                // Connect channel.
+                case MSG_CONNECT_CHANNEL:
+                    mDevice = (BluetoothDevice) msg.obj;
+                    connectChannel();
+                    break;
+                // Disconnect channel.
+                case MSG_DISCONNECT_CHANNEL:
+                    mDevice = (BluetoothDevice) msg.obj;
+                    disconnectChannel();
+                    break;
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+    }
+
+    final Messenger mMessenger = new Messenger(new IncomingHandler());
+
+    /**
+     * Make sure Bluetooth and health profile are available on the Android device.  Stop service
+     * if they are not available.
+     */
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
+            // Bluetooth adapter isn't available.  The client of the service is supposed to
+            // verify that it is available and activate before invoking this service.
+            stopSelf();
+            return;
+        }
+        if (!mBluetoothAdapter.getProfileProxy(this, mBluetoothServiceListener,
+                BluetoothProfile.HEALTH)) {
+            Toast.makeText(this, R.string.bluetooth_health_profile_not_available,
+                    Toast.LENGTH_LONG);
+            stopSelf();
+            return;
+        }
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.d(TAG, "BluetoothHDPService is running.");
+        return START_STICKY;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mMessenger.getBinder();
+    };
+
+    // Register health application through the Bluetooth Health API.
+    private void registerApp(int dataType) {
+        mBluetoothHealth.registerSinkAppConfiguration(TAG, dataType, mHealthCallback);
+    }
+
+    // Unregister health application through the Bluetooth Health API.
+    private void unregisterApp() {
+        mBluetoothHealth.unregisterAppConfiguration(mHealthAppConfig);
+    }
+
+    // Connect channel through the Bluetooth Health API.
+    private void connectChannel() {
+        Log.i(TAG, "connectChannel()");
+        mBluetoothHealth.connectChannelToSource(mDevice, mHealthAppConfig);
+    }
+
+    // Disconnect channel through the Bluetooth Health API.
+    private void disconnectChannel() {
+        Log.i(TAG, "disconnectChannel()");
+        mBluetoothHealth.disconnectChannel(mDevice, mHealthAppConfig, mChannelId);
+    }
+
+    // Callbacks to handle connection set up and disconnection clean up.
+    private final BluetoothProfile.ServiceListener mBluetoothServiceListener =
+            new BluetoothProfile.ServiceListener() {
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            if (profile == BluetoothProfile.HEALTH) {
+                mBluetoothHealth = (BluetoothHealth) proxy;
+                if (Log.isLoggable(TAG, Log.DEBUG))
+                    Log.d(TAG, "onServiceConnected to profile: " + profile);
+            }
+        }
+
+        public void onServiceDisconnected(int profile) {
+            if (profile == BluetoothProfile.HEALTH) {
+                mBluetoothHealth = null;
+            }
+        }
+    };
+
+    private final BluetoothHealthCallback mHealthCallback = new BluetoothHealthCallback() {
+        // Callback to handle application registration and unregistration events.  The service
+        // passes the status back to the UI client.
+        public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
+                int status) {
+            if (status == BluetoothHealth.APP_CONFIG_REGISTRATION_FAILURE) {
+                mHealthAppConfig = null;
+                sendMessage(STATUS_HEALTH_APP_REG, RESULT_FAIL);
+            } else if (status == BluetoothHealth.APP_CONFIG_REGISTRATION_SUCCESS) {
+                mHealthAppConfig = config;
+                sendMessage(STATUS_HEALTH_APP_REG, RESULT_OK);
+            } else if (status == BluetoothHealth.APP_CONFIG_UNREGISTRATION_FAILURE ||
+                    status == BluetoothHealth.APP_CONFIG_UNREGISTRATION_SUCCESS) {
+                sendMessage(STATUS_HEALTH_APP_UNREG,
+                        status == BluetoothHealth.APP_CONFIG_UNREGISTRATION_SUCCESS ?
+                        RESULT_OK : RESULT_FAIL);
+            }
+        }
+
+        // Callback to handle channel connection state changes.
+        // Note that the logic of the state machine may need to be modified based on the HDP device.
+        // When the HDP device is connected, the received file descriptor is passed to the
+        // ReadThread to read the content.
+        public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
+                BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd,
+                int channelId) {
+            if (Log.isLoggable(TAG, Log.DEBUG))
+                Log.d(TAG, String.format("prevState\t%d ----------> newState\t%d",
+                        prevState, newState));
+            if (prevState == BluetoothHealth.STATE_CHANNEL_DISCONNECTED &&
+                    newState == BluetoothHealth.STATE_CHANNEL_CONNECTED) {
+                if (config.equals(mHealthAppConfig)) {
+                    mChannelId = channelId;
+                    sendMessage(STATUS_CREATE_CHANNEL, RESULT_OK);
+                    (new ReadThread(fd)).start();
+                } else {
+                    sendMessage(STATUS_CREATE_CHANNEL, RESULT_FAIL);
+                }
+            } else if (prevState == BluetoothHealth.STATE_CHANNEL_CONNECTING &&
+                       newState == BluetoothHealth.STATE_CHANNEL_DISCONNECTED) {
+                sendMessage(STATUS_CREATE_CHANNEL, RESULT_FAIL);
+            } else if (newState == BluetoothHealth.STATE_CHANNEL_DISCONNECTED) {
+                if (config.equals(mHealthAppConfig)) {
+                    sendMessage(STATUS_DESTROY_CHANNEL, RESULT_OK);
+                } else {
+                    sendMessage(STATUS_DESTROY_CHANNEL, RESULT_FAIL);
+                }
+            }
+        }
+    };
+
+    // Sends an update message to registered UI client.
+    private void sendMessage(int what, int value) {
+        if (mClient == null) {
+            Log.d(TAG, "No clients registered.");
+            return;
+        }
+
+        try {
+            mClient.send(Message.obtain(null, what, value, 0));
+        } catch (RemoteException e) {
+            // Unable to reach client.
+            e.printStackTrace();
+        }
+    }
+
+    // Thread to read incoming data received from the HDP device.  This sample application merely
+    // reads the raw byte from the incoming file descriptor.  The data should be interpreted using
+    // a health manager which implements the IEEE 11073-xxxxx specifications.
+    private class ReadThread extends Thread {
+        private ParcelFileDescriptor mFd;
+
+        public ReadThread(ParcelFileDescriptor fd) {
+            super();
+            mFd = fd;
+        }
+
+        @Override
+        public void run() {
+            FileInputStream fis = new FileInputStream(mFd.getFileDescriptor());
+            final byte data[] = new byte[8192];
+            try {
+                while(fis.read(data) > -1) {
+                    // At this point, the application can pass the raw data to a parser that
+                    // has implemented the IEEE 11073-xxxxx specifications.  Instead, this sample
+                    // simply indicates that some data has been received.
+                    sendMessage(STATUS_READ_DATA, 0);
+                }
+            } catch(IOException ioe) {}
+            if (mFd != null) {
+                try {
+                    mFd.close();
+                } catch (IOException e) { /* Do nothing. */ }
+            }
+            sendMessage(STATUS_READ_DATA_DONE, 0);
+        }
+    }
+}