New public APIs for BluetoothA2dp and BluetoothHeadset profiles.

Change-Id: I1cc4b109542dfd62473cb95797c8c3d0d15725f4
diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java
index be21d46..0496b1f 100644
--- a/framework/java/android/bluetooth/BluetoothHeadset.java
+++ b/framework/java/android/bluetooth/BluetoothHeadset.java
@@ -18,6 +18,8 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -26,63 +28,66 @@
 import android.os.IBinder;
 import android.util.Log;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
 /**
- * The Android Bluetooth API is not finalized, and *will* change. Use at your
- * own risk.
- *
  * Public API for controlling the Bluetooth Headset Service. This includes both
- * Bluetooth Headset and Handsfree (v1.5) profiles. The Headset service will
- * attempt a handsfree connection first, and fall back to headset.
+ * Bluetooth Headset and Handsfree (v1.5) profiles.
  *
- * BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
+ * <p>BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
  * Service via IPC.
  *
- * Creating a BluetoothHeadset object will create a binding with the
- * BluetoothHeadset service. Users of this object should call close() when they
- * are finished with the BluetoothHeadset, so that this proxy object can unbind
- * from the service.
+ * <p> Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothHeadset proxy object. Use
+ * {@link BluetoothAdapter#closeProfileProxy} to close the service connection.
  *
- * This BluetoothHeadset object is not immediately bound to the
- * BluetoothHeadset service. Use the ServiceListener interface to obtain a
- * notification when it is bound, this is especially important if you wish to
- * immediately call methods on BluetoothHeadset after construction.
- *
- * Android only supports one connected Bluetooth Headset at a time.
- *
- * @hide
+ * <p> Android only supports one connected Bluetooth Headset at a time.
+ * Each method is protected with its appropriate permission.
  */
-public final class BluetoothHeadset {
-
+public final class BluetoothHeadset implements BluetoothProfile {
     private static final String TAG = "BluetoothHeadset";
     private static final boolean DBG = false;
 
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_STATE_CHANGED =
-            "android.bluetooth.headset.action.STATE_CHANGED";
     /**
-     * TODO(API release): Consider incorporating as new state in
-     * HEADSET_STATE_CHANGED
+     * Intent used to broadcast the change in connection state of the Headset
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * {@link #EXTRA_STATE} - The current state of the profile.
+     * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
+     * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
+     *
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+        "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED";
+
+    /**
+     * Intent used to broadcast the change in the Audio Connection state of the
+     * A2DP profile.
+     *
+     * <p>This intent will have 3 extras:
+     * {@link #EXTRA_STATE} - The current state of the profile.
+     * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
+     * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
+     *
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED},
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_AUDIO_STATE_CHANGED =
-            "android.bluetooth.headset.action.AUDIO_STATE_CHANGED";
-    public static final String EXTRA_STATE =
-            "android.bluetooth.headset.extra.STATE";
-    public static final String EXTRA_PREVIOUS_STATE =
-            "android.bluetooth.headset.extra.PREVIOUS_STATE";
-    public static final String EXTRA_AUDIO_STATE =
-            "android.bluetooth.headset.extra.AUDIO_STATE";
+        "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
 
-    /** Extra to be used with the Headset State change intent.
-     * This will be used only when Headset state changes to
-     * {@link #STATE_DISCONNECTED} from any previous state.
-     * This extra field is optional and will be used when
-     * we have deterministic information regarding whether
-     * the disconnect was initiated by the remote device or
-     * by the local adapter.
-     */
-    public static final String EXTRA_DISCONNECT_INITIATOR =
-            "android.bluetooth.headset.extra.DISCONNECT_INITIATOR";
 
     /**
      * Broadcast Action: Indicates a headset has posted a vendor-specific event.
@@ -124,100 +129,47 @@
     public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS =
             "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS";
 
+    /*
+     * Headset state when SCO audio is connected
+     * This state can be one of
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+     * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
+     */
+    public static final int STATE_AUDIO_CONNECTED = 10;
 
     /**
-     * TODO(API release): Consider incorporating as new state in
-     * HEADSET_STATE_CHANGED
+     * Headset state when SCO audio is NOT connected
+     * This state can be one of
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+     * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
      */
+    public static final int STATE_AUDIO_DISCONNECTED = 11;
+
+
+    private Context mContext;
+    private ServiceListener mServiceListener;
     private IBluetoothHeadset mService;
-    private final Context mContext;
-    private final ServiceListener mServiceListener;
-
-    /** There was an error trying to obtain the state */
-    public static final int STATE_ERROR        = -1;
-    /** No headset currently connected */
-    public static final int STATE_DISCONNECTED = 0;
-    /** Connection attempt in progress */
-    public static final int STATE_CONNECTING   = 1;
-    /** A headset is currently connected */
-    public static final int STATE_CONNECTED    = 2;
-
-    /** A SCO audio channel is not established */
-    public static final int AUDIO_STATE_DISCONNECTED = 0;
-    /** A SCO audio channel is established */
-    public static final int AUDIO_STATE_CONNECTED = 1;
-
-    public static final int RESULT_FAILURE = 0;
-    public static final int RESULT_SUCCESS = 1;
-    /** Connection canceled before completion. */
-    public static final int RESULT_CANCELED = 2;
-
-    /** Values for {@link #EXTRA_DISCONNECT_INITIATOR} */
-    public static final int REMOTE_DISCONNECT = 0;
-    public static final int LOCAL_DISCONNECT = 1;
-
-
-    /** Default priority for headsets that  for which we will accept
-     * inconing connections and auto-connect */
-    public static final int PRIORITY_AUTO_CONNECT = 1000;
-    /** Default priority for headsets that  for which we will accept
-     * inconing connections but not auto-connect */
-    public static final int PRIORITY_ON = 100;
-    /** Default priority for headsets that should not be auto-connected
-     * and not allow incoming connections. */
-    public static final int PRIORITY_OFF = 0;
-    /** Default priority when not set or when the device is unpaired */
-    public static final int PRIORITY_UNDEFINED = -1;
-
-    /**
-     * An interface for notifying BluetoothHeadset IPC clients when they have
-     * been connected to the BluetoothHeadset service.
-     */
-    public interface ServiceListener {
-        /**
-         * Called to notify the client when this proxy object has been
-         * connected to the BluetoothHeadset service. Clients must wait for
-         * this callback before making IPC calls on the BluetoothHeadset
-         * service.
-         */
-        public void onServiceConnected();
-
-        /**
-         * Called to notify the client that this proxy object has been
-         * disconnected from the BluetoothHeadset service. Clients must not
-         * make IPC calls on the BluetoothHeadset service after this callback.
-         * This callback will currently only occur if the application hosting
-         * the BluetoothHeadset service, but may be called more often in future.
-         */
-        public void onServiceDisconnected();
-    }
+    BluetoothAdapter mAdapter;
 
     /**
      * Create a BluetoothHeadset proxy object.
      */
-    public BluetoothHeadset(Context context, ServiceListener l) {
+    /*package*/ BluetoothHeadset(Context context, ServiceListener l) {
         mContext = context;
         mServiceListener = l;
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
         if (!context.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) {
             Log.e(TAG, "Could not bind to Bluetooth Headset Service");
         }
     }
 
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
-        }
-    }
-
     /**
      * Close the connection to the backing service.
      * Other public functions of BluetoothHeadset will return default error
      * results once close() has been called. Multiple invocations of close()
      * are ok.
      */
-    public synchronized void close() {
+    /*package*/ synchronized void close() {
         if (DBG) log("close()");
         if (mConnection != null) {
             mContext.unbindService(mConnection);
@@ -226,190 +178,212 @@
     }
 
     /**
-     * Get the current state of the Bluetooth Headset service.
-     * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
-     *         object is currently not connected to the Headset service.
+     * {@inheritDoc}
+     * @hide
      */
-    public int getState(BluetoothDevice device) {
-        if (DBG) log("getState()");
-        if (mService != null) {
+    public boolean connect(BluetoothDevice device) {
+        if (DBG) log("connect(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
             try {
-                return mService.getState(device);
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return mService.connect(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return false;
+            }
         }
-        return BluetoothHeadset.STATE_ERROR;
-    }
-
-    /**
-     * Get the BluetoothDevice for the current headset.
-     * @return current headset, or null if not in connected or connecting
-     *         state, or if this proxy object is not connected to the Headset
-     *         service.
-     */
-    public BluetoothDevice getCurrentHeadset() {
-        if (DBG) log("getCurrentHeadset()");
-        if (mService != null) {
-            try {
-                return mService.getCurrentHeadset();
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
-        return null;
-    }
-
-    /**
-     * Request to initiate a connection to a headset.
-     * This call does not block. Fails if a headset is already connecting
-     * or connected.
-     * Initiates auto-connection if device is null. Tries to connect to all
-     * devices with priority greater than PRIORITY_AUTO in descending order.
-     * @param device device to connect to, or null to auto-connect last connected
-     *               headset
-     * @return       false if there was a problem initiating the connection
-     *               procedure, and no further HEADSET_STATE_CHANGED intents
-     *               will be expected.
-     */
-    public boolean connectHeadset(BluetoothDevice device) {
-        if (DBG) log("connectHeadset(" + device + ")");
-        if (mService != null) {
-            try {
-                if (mService.connectHeadset(device)) {
-                    return true;
-                }
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
         return false;
     }
 
     /**
-     * Returns true if the specified headset is connected (does not include
-     * connecting). Returns false if not connected, or if this proxy object
-     * if not currently connected to the headset service.
+     * {@inheritDoc}
+     * @hide
      */
-    public boolean isConnected(BluetoothDevice device) {
-        if (DBG) log("isConnected(" + device + ")");
-        if (mService != null) {
+    public boolean disconnect(BluetoothDevice device) {
+        if (DBG) log("disconnect(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
             try {
-                return mService.isConnected(device);
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return mService.disconnect(device);
+            } catch (RemoteException e) {
+              Log.e(TAG, Log.getStackTraceString(new Throwable()));
+              return false;
+            }
         }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
         return false;
     }
 
     /**
-     * Disconnects the current headset. Currently this call blocks, it may soon
-     * be made asynchornous. Returns false if this proxy object is
-     * not currently connected to the Headset service.
+     * {@inheritDoc}
      */
-    public boolean disconnectHeadset(BluetoothDevice device) {
-        if (DBG) log("disconnectHeadset()");
-        if (mService != null) {
+    public Set<BluetoothDevice> getConnectedDevices() {
+        if (DBG) log("getConnectedDevices()");
+        if (mService != null && isEnabled()) {
             try {
-                mService.disconnectHeadset(device);
-                return true;
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return toDeviceSet(mService.getConnectedDevices());
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return toDeviceSet(new BluetoothDevice[0]);
+            }
         }
-        return false;
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return toDeviceSet(new BluetoothDevice[0]);
     }
 
     /**
-     * Start BT Voice Recognition mode, and set up Bluetooth audio path.
-     * Returns false if there is no headset connected, or if the
-     * connected headset does not support voice recognition, or on
-     * error.
+     * {@inheritDoc}
      */
-    public boolean startVoiceRecognition() {
-        if (DBG) log("startVoiceRecognition()");
-        if (mService != null) {
+    public Set<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+        if (DBG) log("getDevicesMatchingStates()");
+        if (mService != null && isEnabled()) {
             try {
-                return mService.startVoiceRecognition();
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return toDeviceSet(mService.getDevicesMatchingConnectionStates(states));
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return toDeviceSet(new BluetoothDevice[0]);
+            }
         }
-        return false;
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return toDeviceSet(new BluetoothDevice[0]);
     }
 
     /**
-     * Stop BT Voice Recognition mode, and shut down Bluetooth audio path.
-     * Returns false if there is no headset connected, or the connected
-     * headset is not in voice recognition mode, or on error.
+     * {@inheritDoc}
      */
-    public boolean stopVoiceRecognition() {
-        if (DBG) log("stopVoiceRecognition()");
-        if (mService != null) {
+    public int getConnectionState(BluetoothDevice device) {
+        if (DBG) log("getConnectionState(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
             try {
-                return mService.stopVoiceRecognition();
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return mService.getConnectionState(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
         }
-        return false;
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return BluetoothProfile.STATE_DISCONNECTED;
     }
 
     /**
-     * Set priority of headset.
-     * Priority is a non-negative integer. By default paired headsets will have
-     * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0).
-     * Headsets with priority greater than zero will be auto-connected, and
-     * incoming connections will be accepted (if no other headset is
-     * connected).
-     * Auto-connection occurs at the following events: boot, incoming phone
-     * call, outgoing phone call.
-     * Headsets with priority equal to zero, or that are unpaired, are not
-     * auto-connected.
-     * Incoming connections are ignored regardless of priority if there is
-     * already a headset connected.
-     * @param device paired headset
-     * @param priority Integer priority, for example PRIORITY_AUTO or
-     *                 PRIORITY_NONE
-     * @return true if successful, false if there was some error
+     * {@inheritDoc}
+     * @hide
      */
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        if (mService != null) {
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            if (priority != BluetoothProfile.PRIORITY_OFF &&
+                priority != BluetoothProfile.PRIORITY_ON) {
+              return false;
+            }
             try {
                 return mService.setPriority(device, priority);
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return false;
+            }
         }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
         return false;
     }
 
     /**
-     * Get priority of headset.
-     * @param device headset
-     * @return non-negative priority, or negative error code on error
+     * {@inheritDoc}
+     * @hide
      */
     public int getPriority(BluetoothDevice device) {
         if (DBG) log("getPriority(" + device + ")");
-        if (mService != null) {
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
             try {
                 return mService.getPriority(device);
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return PRIORITY_OFF;
+            }
         }
-        return -1;
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return PRIORITY_OFF;
+    }
+
+    /**
+     * Start Bluetooth voice recognition. This methods sends the voice
+     * recognition AT command to the headset and establishes the
+     * audio connection.
+     *
+     * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
+     * {@link #EXTRA_STATE} will be set to {@link #STATE_AUDIO_CONNECTED}
+     * when the audio connection is established.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param device Bluetooth headset
+     * @return false if there is no headset connected of if the
+     *               connected headset doesn't support voice recognition
+     *               or on error, true otherwise
+     */
+    public boolean startVoiceRecognition(BluetoothDevice device) {
+        if (DBG) log("startVoiceRecognition()");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.startVoiceRecognition(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Stop Bluetooth Voice Recognition mode, and shut down the
+     * Bluetooth audio path.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param device Bluetooth headset
+     * @return false if there is no headset connected
+     *               or on error, true otherwise
+     */
+    public boolean stopVoiceRecognition(BluetoothDevice device) {
+        if (DBG) log("stopVoiceRecognition()");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.stopVoiceRecognition(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Check if Bluetooth SCO audio is connected.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param device Bluetooth headset
+     * @return true if SCO is connected,
+     *         false otherwise or on error
+     */
+    public boolean isAudioConnected(BluetoothDevice device) {
+        if (DBG) log("isAudioConnected()");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+              return mService.isAudioConnected(device);
+            } catch (RemoteException e) {
+              Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
     }
 
     /**
@@ -420,24 +394,29 @@
      * boot. This is a good indicator for spammy headset/handsfree units that
      * can keep the device awake by polling for cellular status updates. As a
      * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms
+     *
+     * @param device the bluetooth headset.
      * @return monotonically increasing battery usage hint, or a negative error
      *         code on error
      * @hide
      */
-    public int getBatteryUsageHint() {
+    public int getBatteryUsageHint(BluetoothDevice device) {
         if (DBG) log("getBatteryUsageHint()");
-        if (mService != null) {
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
             try {
-                return mService.getBatteryUsageHint();
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return mService.getBatteryUsageHint(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
         }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
         return -1;
     }
+
     /**
      * Indicates if current platform supports voice dialing over bluetooth SCO.
+     *
      * @return true if voice dialing over bluetooth is supported, false otherwise.
      * @hide
      */
@@ -448,11 +427,13 @@
 
     /**
      * Cancel the outgoing connection.
+     * Note: This is an internal function and shouldn't be exposed
+     *
      * @hide
      */
     public boolean cancelConnectThread() {
         if (DBG) log("cancelConnectThread");
-        if (mService != null) {
+        if (mService != null && isEnabled()) {
             try {
                 return mService.cancelConnectThread();
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -465,11 +446,13 @@
 
     /**
      * Accept the incoming connection.
+     * Note: This is an internal function and shouldn't be exposed
+     *
      * @hide
      */
     public boolean acceptIncomingConnect(BluetoothDevice device) {
         if (DBG) log("acceptIncomingConnect");
-        if (mService != null) {
+        if (mService != null && isEnabled()) {
             try {
                 return mService.acceptIncomingConnect(device);
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -481,12 +464,14 @@
     }
 
     /**
-     * Create the connect thread the incoming connection.
+     * Create the connect thread for the incoming connection.
+     * Note: This is an internal function and shouldn't be exposed
+     *
      * @hide
      */
     public boolean createIncomingConnect(BluetoothDevice device) {
         if (DBG) log("createIncomingConnect");
-        if (mService != null) {
+        if (mService != null && isEnabled()) {
             try {
                 return mService.createIncomingConnect(device);
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -500,11 +485,12 @@
     /**
      * Connect to a Bluetooth Headset.
      * Note: This is an internal function and shouldn't be exposed
+     *
      * @hide
      */
     public boolean connectHeadsetInternal(BluetoothDevice device) {
         if (DBG) log("connectHeadsetInternal");
-        if (mService != null) {
+        if (mService != null && isEnabled()) {
             try {
                 return mService.connectHeadsetInternal(device);
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -518,11 +504,12 @@
     /**
      * Disconnect a Bluetooth Headset.
      * Note: This is an internal function and shouldn't be exposed
+     *
      * @hide
      */
     public boolean disconnectHeadsetInternal(BluetoothDevice device) {
         if (DBG) log("disconnectHeadsetInternal");
-        if (mService != null) {
+        if (mService != null && isEnabled()) {
             try {
                  return mService.disconnectHeadsetInternal(device);
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -532,23 +519,61 @@
         }
         return false;
     }
+
+    /**
+     * Set the audio state of the Headset.
+     * Note: This is an internal function and shouldn't be exposed
+     *
+     * @hide
+     */
+    public boolean setAudioState(BluetoothDevice device, int state) {
+        if (DBG) log("setAudioState");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.setAudioState(device, state);
+            } catch (RemoteException e) {Log.e(TAG, e.toString());}
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+        }
+        return false;
+    }
+
     private ServiceConnection mConnection = new ServiceConnection() {
         public void onServiceConnected(ComponentName className, IBinder service) {
             if (DBG) Log.d(TAG, "Proxy object connected");
             mService = IBluetoothHeadset.Stub.asInterface(service);
+
             if (mServiceListener != null) {
-                mServiceListener.onServiceConnected();
+                mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, BluetoothHeadset.this);
             }
         }
         public void onServiceDisconnected(ComponentName className) {
             if (DBG) Log.d(TAG, "Proxy object disconnected");
             mService = null;
             if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected();
+                mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
             }
         }
     };
 
+    private boolean isEnabled() {
+       if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+       return false;
+    }
+
+    private boolean isValidDevice(BluetoothDevice device) {
+       if (device == null) return false;
+
+       if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+       return false;
+    }
+
+    private Set<BluetoothDevice> toDeviceSet(BluetoothDevice[] devices) {
+       return Collections.unmodifiableSet(
+          new HashSet<BluetoothDevice>(Arrays.asList(devices)));
+    }
+
     private static void log(String msg) {
         Log.d(TAG, msg);
     }