BluetoothOppRfcommListener.java revision 1ac5507790a87810061a19dadec36eb328a222ea
1/*
2 * Copyright (c) 2008-2009, Motorola, Inc.
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * - Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * - Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * - Neither the name of the Motorola, Inc. nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33package com.android.bluetooth.opp;
34
35import java.io.IOException;
36import java.net.ServerSocket;
37import java.net.Socket;
38import java.net.SocketException;
39
40import android.bluetooth.BluetoothAdapter;
41import android.bluetooth.BluetoothServerSocket;
42import android.bluetooth.BluetoothSocket;
43import android.os.Handler;
44import android.os.Message;
45import android.util.Log;
46
47/**
48 * This class listens on OPUSH channel for incoming connection
49 */
50public class BluetoothOppRfcommListener {
51    private static final String TAG = "BtOpp RfcommListener";
52
53    public static final int MSG_INCOMING_BTOPP_CONNECTION = 100;
54
55    private volatile boolean mInterrupted;
56
57    private Thread mSocketAcceptThread;
58
59    private Handler mCallback;
60
61    private static final int ACCEPT_WAIT_TIMEOUT = 5000;
62
63    private static final int CREATE_RETRY_TIME = 10;
64
65    private static final int DEFAULT_OPP_CHANNEL = 12;
66
67    private final int mBtOppRfcommChannel;
68
69    private final BluetoothAdapter mAdapter;
70
71    public BluetoothOppRfcommListener(BluetoothAdapter adapter) {
72        this(adapter, DEFAULT_OPP_CHANNEL);
73    }
74
75    public BluetoothOppRfcommListener(BluetoothAdapter adapter, int channel) {
76        mBtOppRfcommChannel = channel;
77        mAdapter = adapter;
78    }
79
80    public synchronized boolean start(Handler callback) {
81        if (mSocketAcceptThread == null) {
82            mCallback = callback;
83
84            mSocketAcceptThread = new Thread(TAG) {
85
86                public void run() {
87                    if (Constants.USE_TCP_DEBUG) {
88                        ServerSocket mServerSocket = null;
89                        try {
90                            if (Constants.LOGVV) {
91                                Log.v(TAG, "Create ServerSocket on port "
92                                        + Constants.TCP_DEBUG_PORT);
93                            }
94
95                            mServerSocket = new ServerSocket(Constants.TCP_DEBUG_PORT, 1);
96
97                        } catch (IOException e) {
98                            Log.e(TAG, "Error listing on port" + Constants.TCP_DEBUG_PORT);
99                            mInterrupted = true;
100                        }
101                        while (!mInterrupted) {
102                            try {
103                                mServerSocket.setSoTimeout(ACCEPT_WAIT_TIMEOUT);
104                                Socket clientSocket = mServerSocket.accept();
105
106                                if (clientSocket == null) {
107                                    if (Constants.LOGVV) {
108                                        Log.v(TAG, "incomming connection time out");
109                                    }
110                                } else {
111                                    if (Constants.LOGV) {
112                                        Log.v(TAG, "TCP Socket connected!");
113                                    }
114                                    Log.d(TAG, "remote addr is "
115                                            + clientSocket.getRemoteSocketAddress());
116                                    TestTcpTransport transport = new TestTcpTransport(clientSocket);
117                                    Message msg = Message.obtain();
118                                    msg.setTarget(mCallback);
119                                    msg.what = MSG_INCOMING_BTOPP_CONNECTION;
120                                    msg.obj = transport;
121                                    msg.sendToTarget();
122                                }
123                            } catch (SocketException e) {
124                                if (Constants.LOGVV) {
125                                    Log.v(TAG, "Error accept connection " + e);
126                                }
127                            } catch (IOException e) {
128                                if (Constants.LOGVV) {
129                                    Log.v(TAG, "Error accept connection " + e);
130                                }
131                            }
132                        }
133                        if (Constants.LOGV) {
134                            Log.v(TAG, "TCP listen thread finished");
135                        }
136                        try {
137                            mServerSocket.close();
138                        } catch (IOException e) {
139                            Log.e(TAG, "Error close mServerSocker " + e);
140                        }
141                    } else {
142                        BluetoothServerSocket mServerSocket = null;
143                        boolean serverOK = true;
144
145                        /*
146                         * it's possible that create will fail in some cases.
147                         * retry for 10 times
148                         */
149                        for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) {
150                            try {
151                                mServerSocket =
152                                        mAdapter.listenUsingInsecureRfcommOn(mBtOppRfcommChannel);
153                            } catch (IOException e1) {
154                                Log.e(TAG, "Error create RfcommServerSocket " + e1);
155                                serverOK = false;
156                            }
157                            if (!serverOK) {
158                                synchronized (this) {
159                                    try {
160                                        if (Constants.LOGVV) {
161                                            Log.v(TAG, "wait 3 seconds");
162                                        }
163                                        Thread.sleep(3000);
164                                    } catch (InterruptedException e) {
165                                        Log.e(TAG, "socketAcceptThread thread was interrupted (3)");
166                                        mInterrupted = true;
167                                    }
168                                }
169                            } else {
170                                break;
171                            }
172                        }
173                        if (!serverOK) {
174                            Log.e(TAG, "Error start listening after " + CREATE_RETRY_TIME + " try");
175                            mInterrupted = true;
176                        }
177                        if (!mInterrupted) {
178                            Log.i(TAG, "Accept thread started on channel " + mBtOppRfcommChannel);
179                        }
180                        BluetoothSocket clientSocket;
181                        while (!mInterrupted) {
182                            try {
183                                clientSocket = mServerSocket.accept(ACCEPT_WAIT_TIMEOUT);
184                                Log.i(TAG, "Accepted connectoin from " + clientSocket.getRemoteDevice());
185                                BluetoothOppRfcommTransport transport = new BluetoothOppRfcommTransport(
186                                        clientSocket);
187                                Message msg = Message.obtain();
188                                msg.setTarget(mCallback);
189                                msg.what = MSG_INCOMING_BTOPP_CONNECTION;
190                                msg.obj = transport;
191                                msg.sendToTarget();
192                            } catch (IOException e) {
193                                //TODO later accept should not throw exception
194                                if (Constants.LOGVV) {
195                                    //Log.v(TAG, "Error accept connection " + e);
196                                }
197                            }
198                        }
199                        try {
200                            if (mServerSocket != null) {
201                                if (Constants.LOGVV) {
202                                    Log.v(TAG, "close mServerSocket");
203                                }
204                                mServerSocket.close();
205                            }
206                        } catch (IOException e) {
207                            Log.e(TAG, "Errro close mServerSocket " + e);
208                        }
209                        Log.i(TAG, "BluetoothSocket listen thread finished");
210                    }
211                }
212            };
213            mInterrupted = false;
214            mSocketAcceptThread.start();
215        }
216        return true;
217    }
218
219    public synchronized void stop() {
220        if (mSocketAcceptThread != null) {
221            Log.i(TAG, "stopping Accept Thread");
222
223            mInterrupted = true;
224            try {
225                mSocketAcceptThread.interrupt();
226                if (Constants.LOGVV) {
227                    Log.v(TAG, "waiting for thread to terminate");
228                }
229                mSocketAcceptThread.join();
230                mSocketAcceptThread = null;
231                mCallback = null;
232            } catch (InterruptedException e) {
233                if (Constants.LOGVV) {
234                    Log.v(TAG, "Interrupted waiting for Accept Thread to join");
235                }
236            }
237        }
238    }
239}
240