Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.bluetooth;
     18 
     19 import android.annotation.SdkConstant;
     20 import android.annotation.SdkConstant.SdkConstantType;
     21 import android.content.ComponentName;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.ServiceConnection;
     25 import android.os.IBinder;
     26 import android.os.RemoteException;
     27 import android.util.Log;
     28 
     29 import java.util.ArrayList;
     30 import java.util.List;
     31 
     32 /**
     33  * Public API for controlling the Bluetooth Headset Service. This includes both
     34  * Bluetooth Headset and Handsfree (v1.5) profiles.
     35  *
     36  * <p>BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
     37  * Service via IPC.
     38  *
     39  * <p> Use {@link BluetoothAdapter#getProfileProxy} to get
     40  * the BluetoothHeadset proxy object. Use
     41  * {@link BluetoothAdapter#closeProfileProxy} to close the service connection.
     42  *
     43  * <p> Android only supports one connected Bluetooth Headset at a time.
     44  * Each method is protected with its appropriate permission.
     45  */
     46 public final class BluetoothHeadset implements BluetoothProfile {
     47     private static final String TAG = "BluetoothHeadset";
     48     private static final boolean DBG = false;
     49 
     50     /**
     51      * Intent used to broadcast the change in connection state of the Headset
     52      * profile.
     53      *
     54      * <p>This intent will have 3 extras:
     55      * <ul>
     56      *   <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
     57      *   <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
     58      *   <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
     59      * </ul>
     60      * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
     61      * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
     62      * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
     63      *
     64      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
     65      * receive.
     66      */
     67     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     68     public static final String ACTION_CONNECTION_STATE_CHANGED =
     69         "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED";
     70 
     71     /**
     72      * Intent used to broadcast the change in the Audio Connection state of the
     73      * A2DP profile.
     74      *
     75      * <p>This intent will have 3 extras:
     76      * <ul>
     77      *   <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
     78      *   <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
     79      *   <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
     80      * </ul>
     81      * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
     82      * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED},
     83      *
     84      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
     85      * to receive.
     86      */
     87     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     88     public static final String ACTION_AUDIO_STATE_CHANGED =
     89         "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
     90 
     91 
     92     /**
     93      * Intent used to broadcast that the headset has posted a
     94      * vendor-specific event.
     95      *
     96      * <p>This intent will have 4 extras and 1 category.
     97      * <ul>
     98      *  <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device
     99      *       </li>
    100      *  <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor
    101      *       specific command </li>
    102      *  <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT
    103      *       command type which can be one of  {@link #AT_CMD_TYPE_READ},
    104      *       {@link #AT_CMD_TYPE_TEST}, or {@link #AT_CMD_TYPE_SET},
    105      *       {@link #AT_CMD_TYPE_BASIC},{@link #AT_CMD_TYPE_ACTION}. </li>
    106      *  <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command
    107      *       arguments. </li>
    108      * </ul>
    109      *
    110      *<p> The category is the Company ID of the vendor defining the
    111      * vendor-specific command. {@link BluetoothAssignedNumbers}
    112      *
    113      * For example, for Plantronics specific events
    114      * Category will be {@link #VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY}.55
    115      *
    116      * <p> For example, an AT+XEVENT=foo,3 will get translated into
    117      * <ul>
    118      *   <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT </li>
    119      *   <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET </li>
    120      *   <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3 </li>
    121      * </ul>
    122      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
    123      * to receive.
    124      */
    125     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    126     public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT =
    127             "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT";
    128 
    129     /**
    130      * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
    131      * intents that contains the name of the vendor-specific command.
    132      */
    133     public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD =
    134             "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD";
    135 
    136     /**
    137      * An int extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
    138      * intents that contains the AT command type of the vendor-specific command.
    139      */
    140     public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE =
    141             "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE";
    142 
    143     /**
    144      * AT command type READ used with
    145      * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
    146      * For example, AT+VGM?. There are no arguments for this command type.
    147      */
    148     public static final int AT_CMD_TYPE_READ = 0;
    149 
    150     /**
    151      * AT command type TEST used with
    152      * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
    153      * For example, AT+VGM=?. There are no arguments for this command type.
    154      */
    155     public static final int AT_CMD_TYPE_TEST = 1;
    156 
    157     /**
    158      * AT command type SET used with
    159      * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
    160      * For example, AT+VGM=<args>.
    161      */
    162     public static final int AT_CMD_TYPE_SET = 2;
    163 
    164     /**
    165      * AT command type BASIC used with
    166      * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
    167      * For example, ATD. Single character commands and everything following the
    168      * character are arguments.
    169      */
    170     public static final int AT_CMD_TYPE_BASIC = 3;
    171 
    172     /**
    173      * AT command type ACTION used with
    174      * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
    175      * For example, AT+CHUP. There are no arguments for action commands.
    176      */
    177     public static final int AT_CMD_TYPE_ACTION = 4;
    178 
    179     /**
    180      * A Parcelable String array extra field in
    181      * {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intents that contains
    182      * the arguments to the vendor-specific command.
    183      */
    184     public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS =
    185             "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS";
    186 
    187     /**
    188      * The intent category to be used with {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
    189      * for the companyId
    190      */
    191     public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY  =
    192             "android.bluetooth.headset.intent.category.companyid";
    193 
    194     /**
    195      * Headset state when SCO audio is not connected.
    196      * This state can be one of
    197      * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
    198      * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
    199      */
    200     public static final int STATE_AUDIO_DISCONNECTED = 10;
    201 
    202     /**
    203      * Headset state when SCO audio is connecting.
    204      * This state can be one of
    205      * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
    206      * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
    207      */
    208     public static final int STATE_AUDIO_CONNECTING = 11;
    209 
    210     /**
    211      * Headset state when SCO audio is connected.
    212      * This state can be one of
    213      * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
    214      * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
    215      */
    216     public static final int STATE_AUDIO_CONNECTED = 12;
    217 
    218 
    219     private Context mContext;
    220     private ServiceListener mServiceListener;
    221     private IBluetoothHeadset mService;
    222     BluetoothAdapter mAdapter;
    223 
    224     /**
    225      * Create a BluetoothHeadset proxy object.
    226      */
    227     /*package*/ BluetoothHeadset(Context context, ServiceListener l) {
    228         mContext = context;
    229         mServiceListener = l;
    230         mAdapter = BluetoothAdapter.getDefaultAdapter();
    231         if (!context.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) {
    232             Log.e(TAG, "Could not bind to Bluetooth Headset Service");
    233         }
    234     }
    235 
    236     /**
    237      * Close the connection to the backing service.
    238      * Other public functions of BluetoothHeadset will return default error
    239      * results once close() has been called. Multiple invocations of close()
    240      * are ok.
    241      */
    242     /*package*/ synchronized void close() {
    243         if (DBG) log("close()");
    244         if (mConnection != null) {
    245             mContext.unbindService(mConnection);
    246             mConnection = null;
    247         }
    248         mServiceListener = null;
    249     }
    250 
    251     /**
    252      * Initiate connection to a profile of the remote bluetooth device.
    253      *
    254      * <p> Currently, the system supports only 1 connection to the
    255      * headset/handsfree profile. The API will automatically disconnect connected
    256      * devices before connecting.
    257      *
    258      * <p> This API returns false in scenarios like the profile on the
    259      * device is already connected or Bluetooth is not turned on.
    260      * When this API returns true, it is guaranteed that
    261      * connection state intent for the profile will be broadcasted with
    262      * the state. Users can get the connection state of the profile
    263      * from this intent.
    264      *
    265      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
    266      * permission.
    267      *
    268      * @param device Remote Bluetooth Device
    269      * @return false on immediate error,
    270      *               true otherwise
    271      * @hide
    272      */
    273     public boolean connect(BluetoothDevice device) {
    274         if (DBG) log("connect(" + device + ")");
    275         if (mService != null && isEnabled() &&
    276             isValidDevice(device)) {
    277             try {
    278                 return mService.connect(device);
    279             } catch (RemoteException e) {
    280                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    281                 return false;
    282             }
    283         }
    284         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    285         return false;
    286     }
    287 
    288     /**
    289      * Initiate disconnection from a profile
    290      *
    291      * <p> This API will return false in scenarios like the profile on the
    292      * Bluetooth device is not in connected state etc. When this API returns,
    293      * true, it is guaranteed that the connection state change
    294      * intent will be broadcasted with the state. Users can get the
    295      * disconnection state of the profile from this intent.
    296      *
    297      * <p> If the disconnection is initiated by a remote device, the state
    298      * will transition from {@link #STATE_CONNECTED} to
    299      * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
    300      * host (local) device the state will transition from
    301      * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
    302      * state {@link #STATE_DISCONNECTED}. The transition to
    303      * {@link #STATE_DISCONNECTING} can be used to distinguish between the
    304      * two scenarios.
    305      *
    306      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
    307      * permission.
    308      *
    309      * @param device Remote Bluetooth Device
    310      * @return false on immediate error,
    311      *               true otherwise
    312      * @hide
    313      */
    314     public boolean disconnect(BluetoothDevice device) {
    315         if (DBG) log("disconnect(" + device + ")");
    316         if (mService != null && isEnabled() &&
    317             isValidDevice(device)) {
    318             try {
    319                 return mService.disconnect(device);
    320             } catch (RemoteException e) {
    321               Log.e(TAG, Log.getStackTraceString(new Throwable()));
    322               return false;
    323             }
    324         }
    325         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    326         return false;
    327     }
    328 
    329     /**
    330      * {@inheritDoc}
    331      */
    332     public List<BluetoothDevice> getConnectedDevices() {
    333         if (DBG) log("getConnectedDevices()");
    334         if (mService != null && isEnabled()) {
    335             try {
    336                 return mService.getConnectedDevices();
    337             } catch (RemoteException e) {
    338                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    339                 return new ArrayList<BluetoothDevice>();
    340             }
    341         }
    342         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    343         return new ArrayList<BluetoothDevice>();
    344     }
    345 
    346     /**
    347      * {@inheritDoc}
    348      */
    349     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    350         if (DBG) log("getDevicesMatchingStates()");
    351         if (mService != null && isEnabled()) {
    352             try {
    353                 return mService.getDevicesMatchingConnectionStates(states);
    354             } catch (RemoteException e) {
    355                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    356                 return new ArrayList<BluetoothDevice>();
    357             }
    358         }
    359         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    360         return new ArrayList<BluetoothDevice>();
    361     }
    362 
    363     /**
    364      * {@inheritDoc}
    365      */
    366     public int getConnectionState(BluetoothDevice device) {
    367         if (DBG) log("getConnectionState(" + device + ")");
    368         if (mService != null && isEnabled() &&
    369             isValidDevice(device)) {
    370             try {
    371                 return mService.getConnectionState(device);
    372             } catch (RemoteException e) {
    373                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    374                 return BluetoothProfile.STATE_DISCONNECTED;
    375             }
    376         }
    377         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    378         return BluetoothProfile.STATE_DISCONNECTED;
    379     }
    380 
    381     /**
    382      * Set priority of the profile
    383      *
    384      * <p> The device should already be paired.
    385      *  Priority can be one of {@link #PRIORITY_ON} or
    386      * {@link #PRIORITY_OFF},
    387      *
    388      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
    389      * permission.
    390      *
    391      * @param device Paired bluetooth device
    392      * @param priority
    393      * @return true if priority is set, false on error
    394      * @hide
    395      */
    396     public boolean setPriority(BluetoothDevice device, int priority) {
    397         if (DBG) log("setPriority(" + device + ", " + priority + ")");
    398         if (mService != null && isEnabled() &&
    399             isValidDevice(device)) {
    400             if (priority != BluetoothProfile.PRIORITY_OFF &&
    401                 priority != BluetoothProfile.PRIORITY_ON) {
    402               return false;
    403             }
    404             try {
    405                 return mService.setPriority(device, priority);
    406             } catch (RemoteException e) {
    407                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    408                 return false;
    409             }
    410         }
    411         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    412         return false;
    413     }
    414 
    415     /**
    416      * Get the priority of the profile.
    417      *
    418      * <p> The priority can be any of:
    419      * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
    420      * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
    421      *
    422      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
    423      *
    424      * @param device Bluetooth device
    425      * @return priority of the device
    426      * @hide
    427      */
    428     public int getPriority(BluetoothDevice device) {
    429         if (DBG) log("getPriority(" + device + ")");
    430         if (mService != null && isEnabled() &&
    431             isValidDevice(device)) {
    432             try {
    433                 return mService.getPriority(device);
    434             } catch (RemoteException e) {
    435                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    436                 return PRIORITY_OFF;
    437             }
    438         }
    439         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    440         return PRIORITY_OFF;
    441     }
    442 
    443     /**
    444      * Start Bluetooth voice recognition. This methods sends the voice
    445      * recognition AT command to the headset and establishes the
    446      * audio connection.
    447      *
    448      * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
    449      * If this function returns true, this intent will be broadcasted with
    450      * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
    451      *
    452      * <p> {@link #EXTRA_STATE} will transition from
    453      * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
    454      * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
    455      * in case of failure to establish the audio connection.
    456      *
    457      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
    458      *
    459      * @param device Bluetooth headset
    460      * @return false if there is no headset connected of if the
    461      *               connected headset doesn't support voice recognition
    462      *               or on error, true otherwise
    463      */
    464     public boolean startVoiceRecognition(BluetoothDevice device) {
    465         if (DBG) log("startVoiceRecognition()");
    466         if (mService != null && isEnabled() &&
    467             isValidDevice(device)) {
    468             try {
    469                 return mService.startVoiceRecognition(device);
    470             } catch (RemoteException e) {
    471                 Log.e(TAG,  Log.getStackTraceString(new Throwable()));
    472             }
    473         }
    474         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    475         return false;
    476     }
    477 
    478     /**
    479      * Stop Bluetooth Voice Recognition mode, and shut down the
    480      * Bluetooth audio path.
    481      *
    482      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
    483      *
    484      * @param device Bluetooth headset
    485      * @return false if there is no headset connected
    486      *               or on error, true otherwise
    487      */
    488     public boolean stopVoiceRecognition(BluetoothDevice device) {
    489         if (DBG) log("stopVoiceRecognition()");
    490         if (mService != null && isEnabled() &&
    491             isValidDevice(device)) {
    492             try {
    493                 return mService.stopVoiceRecognition(device);
    494             } catch (RemoteException e) {
    495                 Log.e(TAG,  Log.getStackTraceString(new Throwable()));
    496             }
    497         }
    498         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    499         return false;
    500     }
    501 
    502     /**
    503      * Check if Bluetooth SCO audio is connected.
    504      *
    505      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
    506      *
    507      * @param device Bluetooth headset
    508      * @return true if SCO is connected,
    509      *         false otherwise or on error
    510      */
    511     public boolean isAudioConnected(BluetoothDevice device) {
    512         if (DBG) log("isAudioConnected()");
    513         if (mService != null && isEnabled() &&
    514             isValidDevice(device)) {
    515             try {
    516               return mService.isAudioConnected(device);
    517             } catch (RemoteException e) {
    518               Log.e(TAG,  Log.getStackTraceString(new Throwable()));
    519             }
    520         }
    521         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    522         return false;
    523     }
    524 
    525     /**
    526      * Get battery usage hint for Bluetooth Headset service.
    527      * This is a monotonically increasing integer. Wraps to 0 at
    528      * Integer.MAX_INT, and at boot.
    529      * Current implementation returns the number of AT commands handled since
    530      * boot. This is a good indicator for spammy headset/handsfree units that
    531      * can keep the device awake by polling for cellular status updates. As a
    532      * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms
    533      *
    534      * @param device the bluetooth headset.
    535      * @return monotonically increasing battery usage hint, or a negative error
    536      *         code on error
    537      * @hide
    538      */
    539     public int getBatteryUsageHint(BluetoothDevice device) {
    540         if (DBG) log("getBatteryUsageHint()");
    541         if (mService != null && isEnabled() &&
    542             isValidDevice(device)) {
    543             try {
    544                 return mService.getBatteryUsageHint(device);
    545             } catch (RemoteException e) {
    546                 Log.e(TAG,  Log.getStackTraceString(new Throwable()));
    547             }
    548         }
    549         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    550         return -1;
    551     }
    552 
    553     /**
    554      * Indicates if current platform supports voice dialing over bluetooth SCO.
    555      *
    556      * @return true if voice dialing over bluetooth is supported, false otherwise.
    557      * @hide
    558      */
    559     public static boolean isBluetoothVoiceDialingEnabled(Context context) {
    560         return context.getResources().getBoolean(
    561                 com.android.internal.R.bool.config_bluetooth_sco_off_call);
    562     }
    563 
    564     /**
    565      * Cancel the outgoing connection.
    566      * Note: This is an internal function and shouldn't be exposed
    567      *
    568      * @hide
    569      */
    570     public boolean cancelConnectThread() {
    571         if (DBG) log("cancelConnectThread");
    572         if (mService != null && isEnabled()) {
    573             try {
    574                 return mService.cancelConnectThread();
    575             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    576         } else {
    577             Log.w(TAG, "Proxy not attached to service");
    578             if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
    579         }
    580         return false;
    581     }
    582 
    583     /**
    584      * Accept the incoming connection.
    585      * Note: This is an internal function and shouldn't be exposed
    586      *
    587      * @hide
    588      */
    589     public boolean acceptIncomingConnect(BluetoothDevice device) {
    590         if (DBG) log("acceptIncomingConnect");
    591         if (mService != null && isEnabled()) {
    592             try {
    593                 return mService.acceptIncomingConnect(device);
    594             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    595         } else {
    596             Log.w(TAG, "Proxy not attached to service");
    597             if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
    598         }
    599         return false;
    600     }
    601 
    602     /**
    603      * Create the connect thread for the incoming connection.
    604      * Note: This is an internal function and shouldn't be exposed
    605      *
    606      * @hide
    607      */
    608     public boolean createIncomingConnect(BluetoothDevice device) {
    609         if (DBG) log("createIncomingConnect");
    610         if (mService != null && isEnabled()) {
    611             try {
    612                 return mService.createIncomingConnect(device);
    613             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    614         } else {
    615             Log.w(TAG, "Proxy not attached to service");
    616             if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
    617         }
    618         return false;
    619     }
    620 
    621     /**
    622      * Reject the incoming connection.
    623      * @hide
    624      */
    625     public boolean rejectIncomingConnect(BluetoothDevice device) {
    626         if (DBG) log("rejectIncomingConnect");
    627         if (mService != null) {
    628             try {
    629                 return mService.rejectIncomingConnect(device);
    630             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    631         } else {
    632             Log.w(TAG, "Proxy not attached to service");
    633             if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
    634         }
    635         return false;
    636     }
    637 
    638     /**
    639      * Connect to a Bluetooth Headset.
    640      * Note: This is an internal function and shouldn't be exposed
    641      *
    642      * @hide
    643      */
    644     public boolean connectHeadsetInternal(BluetoothDevice device) {
    645         if (DBG) log("connectHeadsetInternal");
    646         if (mService != null && isEnabled()) {
    647             try {
    648                 return mService.connectHeadsetInternal(device);
    649             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    650         } else {
    651             Log.w(TAG, "Proxy not attached to service");
    652             if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
    653         }
    654         return false;
    655     }
    656 
    657     /**
    658      * Disconnect a Bluetooth Headset.
    659      * Note: This is an internal function and shouldn't be exposed
    660      *
    661      * @hide
    662      */
    663     public boolean disconnectHeadsetInternal(BluetoothDevice device) {
    664         if (DBG) log("disconnectHeadsetInternal");
    665         if (mService != null && !isDisabled()) {
    666             try {
    667                  return mService.disconnectHeadsetInternal(device);
    668             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    669         } else {
    670             Log.w(TAG, "Proxy not attached to service");
    671             if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
    672         }
    673         return false;
    674     }
    675 
    676     /**
    677      * Set the audio state of the Headset.
    678      * Note: This is an internal function and shouldn't be exposed
    679      *
    680      * @hide
    681      */
    682     public boolean setAudioState(BluetoothDevice device, int state) {
    683         if (DBG) log("setAudioState");
    684         if (mService != null && !isDisabled()) {
    685             try {
    686                 return mService.setAudioState(device, state);
    687             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    688         } else {
    689             Log.w(TAG, "Proxy not attached to service");
    690             if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
    691         }
    692         return false;
    693     }
    694 
    695     /**
    696      * Get the current audio state of the Headset.
    697      * Note: This is an internal function and shouldn't be exposed
    698      *
    699      * @hide
    700      */
    701     public int getAudioState(BluetoothDevice device) {
    702         if (DBG) log("getAudioState");
    703         if (mService != null && !isDisabled()) {
    704             try {
    705                 return mService.getAudioState(device);
    706             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    707         } else {
    708             Log.w(TAG, "Proxy not attached to service");
    709             if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
    710         }
    711         return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
    712     }
    713 
    714     /**
    715      * Initiates a SCO channel connection with the headset (if connected).
    716      * Also initiates a virtual voice call for Handsfree devices as many devices
    717      * do not accept SCO audio without a call.
    718      * This API allows the handsfree device to be used for routing non-cellular
    719      * call audio.
    720      *
    721      * @param device Remote Bluetooth Device
    722      * @return true if successful, false if there was some error.
    723      * @hide
    724      */
    725     public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
    726         if (DBG) log("startScoUsingVirtualVoiceCall()");
    727         if (mService != null && isEnabled() && isValidDevice(device)) {
    728             try {
    729                 return mService.startScoUsingVirtualVoiceCall(device);
    730             } catch (RemoteException e) {
    731                 Log.e(TAG, e.toString());
    732             }
    733         } else {
    734             Log.w(TAG, "Proxy not attached to service");
    735             if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
    736         }
    737         return false;
    738     }
    739 
    740     /**
    741      * Terminates an ongoing SCO connection and the associated virtual
    742      * call.
    743      *
    744      * @param device Remote Bluetooth Device
    745      * @return true if successful, false if there was some error.
    746      * @hide
    747      */
    748     public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
    749         if (DBG) log("stopScoUsingVirtualVoiceCall()");
    750         if (mService != null && isEnabled() && isValidDevice(device)) {
    751             try {
    752                 return mService.stopScoUsingVirtualVoiceCall(device);
    753             } catch (RemoteException e) {
    754                 Log.e(TAG, e.toString());
    755             }
    756         } else {
    757             Log.w(TAG, "Proxy not attached to service");
    758             if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
    759         }
    760         return false;
    761     }
    762 
    763     private ServiceConnection mConnection = new ServiceConnection() {
    764         public void onServiceConnected(ComponentName className, IBinder service) {
    765             if (DBG) Log.d(TAG, "Proxy object connected");
    766             mService = IBluetoothHeadset.Stub.asInterface(service);
    767 
    768             if (mServiceListener != null) {
    769                 mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, BluetoothHeadset.this);
    770             }
    771         }
    772         public void onServiceDisconnected(ComponentName className) {
    773             if (DBG) Log.d(TAG, "Proxy object disconnected");
    774             mService = null;
    775             if (mServiceListener != null) {
    776                 mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
    777             }
    778         }
    779     };
    780 
    781     private boolean isEnabled() {
    782        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
    783        return false;
    784     }
    785 
    786     private boolean isDisabled() {
    787        if (mAdapter.getState() == BluetoothAdapter.STATE_OFF) return true;
    788        return false;
    789     }
    790 
    791     private boolean isValidDevice(BluetoothDevice device) {
    792        if (device == null) return false;
    793 
    794        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
    795        return false;
    796     }
    797 
    798     private static void log(String msg) {
    799         Log.d(TAG, msg);
    800     }
    801 }
    802