Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2007 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 java.lang.Thread;
     20 
     21 import android.os.Message;
     22 import android.os.Handler;
     23 import android.util.Log;
     24 
     25 /**
     26  * Listens for incoming RFCOMM connection for the headset / handsfree service.
     27  *
     28  * TODO: Use the new generic BluetoothSocket class instead of this legacy code
     29  *
     30  * @hide
     31  */
     32 public final class BluetoothAudioGateway {
     33     private static final String TAG = "BT Audio Gateway";
     34     private static final boolean DBG = false;
     35 
     36     private int mNativeData;
     37     static { classInitNative(); }
     38 
     39     /* in */
     40     private int mHandsfreeAgRfcommChannel = -1;
     41     private int mHeadsetAgRfcommChannel   = -1;
     42 
     43     /* out - written by native code */
     44     private String mConnectingHeadsetAddress;
     45     private int mConnectingHeadsetRfcommChannel; /* -1 when not connected */
     46     private int mConnectingHeadsetSocketFd;
     47     private String mConnectingHandsfreeAddress;
     48     private int mConnectingHandsfreeRfcommChannel; /* -1 when not connected */
     49     private int mConnectingHandsfreeSocketFd;
     50     private int mTimeoutRemainingMs; /* in/out */
     51 
     52     private final BluetoothAdapter mAdapter;
     53 
     54     public static final int DEFAULT_HF_AG_CHANNEL = 10;
     55     public static final int DEFAULT_HS_AG_CHANNEL = 11;
     56 
     57     public BluetoothAudioGateway(BluetoothAdapter adapter) {
     58         this(adapter, DEFAULT_HF_AG_CHANNEL, DEFAULT_HS_AG_CHANNEL);
     59     }
     60 
     61     public BluetoothAudioGateway(BluetoothAdapter adapter, int handsfreeAgRfcommChannel,
     62                 int headsetAgRfcommChannel) {
     63         mAdapter = adapter;
     64         mHandsfreeAgRfcommChannel = handsfreeAgRfcommChannel;
     65         mHeadsetAgRfcommChannel = headsetAgRfcommChannel;
     66         initializeNativeDataNative();
     67     }
     68 
     69     private Thread mConnectThead;
     70     private volatile boolean mInterrupted;
     71     private static final int SELECT_WAIT_TIMEOUT = 1000;
     72 
     73     private Handler mCallback;
     74 
     75     public class IncomingConnectionInfo {
     76         public BluetoothAdapter mAdapter;
     77         public BluetoothDevice mRemoteDevice;
     78         public int mSocketFd;
     79         public int mRfcommChan;
     80         IncomingConnectionInfo(BluetoothAdapter adapter, BluetoothDevice remoteDevice,
     81                 int socketFd, int rfcommChan) {
     82             mAdapter = adapter;
     83             mRemoteDevice = remoteDevice;
     84             mSocketFd = socketFd;
     85             mRfcommChan = rfcommChan;
     86         }
     87     }
     88 
     89     public static final int MSG_INCOMING_HEADSET_CONNECTION   = 100;
     90     public static final int MSG_INCOMING_HANDSFREE_CONNECTION = 101;
     91 
     92     public synchronized boolean start(Handler callback) {
     93 
     94         if (mConnectThead == null) {
     95             mCallback = callback;
     96             mConnectThead = new Thread(TAG) {
     97                     public void run() {
     98                         if (DBG) log("Connect Thread starting");
     99                         while (!mInterrupted) {
    100                             //Log.i(TAG, "waiting for connect");
    101                             mConnectingHeadsetRfcommChannel = -1;
    102                             mConnectingHandsfreeRfcommChannel = -1;
    103                             if (waitForHandsfreeConnectNative(SELECT_WAIT_TIMEOUT) == false) {
    104                                 if (mTimeoutRemainingMs > 0) {
    105                                     try {
    106                                         Log.i(TAG, "select thread timed out, but " +
    107                                               mTimeoutRemainingMs + "ms of waiting remain.");
    108                                         Thread.sleep(mTimeoutRemainingMs);
    109                                     } catch (InterruptedException e) {
    110                                         Log.i(TAG, "select thread was interrupted (2), exiting");
    111                                         mInterrupted = true;
    112                                     }
    113                                 }
    114                             }
    115                             else {
    116                                 Log.i(TAG, "connect notification!");
    117                                 /* A device connected (most likely just one, but
    118                                    it is possible for two separate devices, one
    119                                    a headset and one a handsfree, to connect
    120                                    simultaneously.
    121                                 */
    122                                 if (mConnectingHeadsetRfcommChannel >= 0) {
    123                                     Log.i(TAG, "Incoming connection from headset " +
    124                                           mConnectingHeadsetAddress + " on channel " +
    125                                           mConnectingHeadsetRfcommChannel);
    126                                     Message msg = Message.obtain(mCallback);
    127                                     msg.what = MSG_INCOMING_HEADSET_CONNECTION;
    128                                     msg.obj = new IncomingConnectionInfo(
    129                                         mAdapter,
    130                                         mAdapter.getRemoteDevice(mConnectingHeadsetAddress),
    131                                         mConnectingHeadsetSocketFd,
    132                                         mConnectingHeadsetRfcommChannel);
    133                                     msg.sendToTarget();
    134                                 }
    135                                 if (mConnectingHandsfreeRfcommChannel >= 0) {
    136                                     Log.i(TAG, "Incoming connection from handsfree " +
    137                                           mConnectingHandsfreeAddress + " on channel " +
    138                                           mConnectingHandsfreeRfcommChannel);
    139                                     Message msg = Message.obtain();
    140                                     msg.setTarget(mCallback);
    141                                     msg.what = MSG_INCOMING_HANDSFREE_CONNECTION;
    142                                     msg.obj = new IncomingConnectionInfo(
    143                                         mAdapter,
    144                                         mAdapter.getRemoteDevice(mConnectingHandsfreeAddress),
    145                                         mConnectingHandsfreeSocketFd,
    146                                         mConnectingHandsfreeRfcommChannel);
    147                                     msg.sendToTarget();
    148                                 }
    149                             }
    150                         }
    151                         if (DBG) log("Connect Thread finished");
    152                     }
    153                 };
    154 
    155             if (setUpListeningSocketsNative() == false) {
    156                 Log.e(TAG, "Could not set up listening socket, exiting");
    157                 return false;
    158             }
    159 
    160             mInterrupted = false;
    161             mConnectThead.start();
    162         }
    163 
    164         return true;
    165     }
    166 
    167     public synchronized void stop() {
    168         if (mConnectThead != null) {
    169             if (DBG) log("stopping Connect Thread");
    170             mInterrupted = true;
    171             try {
    172                 mConnectThead.interrupt();
    173                 if (DBG) log("waiting for thread to terminate");
    174                 mConnectThead.join();
    175                 mConnectThead = null;
    176                 mCallback = null;
    177                 tearDownListeningSocketsNative();
    178             } catch (InterruptedException e) {
    179                 Log.w(TAG, "Interrupted waiting for Connect Thread to join");
    180             }
    181         }
    182     }
    183 
    184     protected void finalize() throws Throwable {
    185         try {
    186             cleanupNativeDataNative();
    187         } finally {
    188             super.finalize();
    189         }
    190     }
    191 
    192     private static native void classInitNative();
    193     private native void initializeNativeDataNative();
    194     private native void cleanupNativeDataNative();
    195     private native boolean waitForHandsfreeConnectNative(int timeoutMs);
    196     private native boolean setUpListeningSocketsNative();
    197     private native void tearDownListeningSocketsNative();
    198 
    199     private static void log(String msg) {
    200         Log.d(TAG, msg);
    201     }
    202 }
    203