BluetoothOppRfcommListener.java revision ce4d93666275df294cb073fe41de5b85932570a8
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 = "BtOppRfcommListener";
52    private static final boolean D = Constants.DEBUG;
53    private static final boolean V = Constants.VERBOSE;
54
55    public static final int MSG_INCOMING_BTOPP_CONNECTION = 100;
56
57    private volatile boolean mInterrupted;
58
59    private Thread mSocketAcceptThread;
60
61    private Handler mCallback;
62
63    private static final int ACCEPT_WAIT_TIMEOUT = 5000;
64
65    private static final int CREATE_RETRY_TIME = 10;
66
67    private static final int DEFAULT_OPP_CHANNEL = 12;
68
69    private final int mBtOppRfcommChannel;
70
71    private final BluetoothAdapter mAdapter;
72
73    public BluetoothOppRfcommListener(BluetoothAdapter adapter) {
74        this(adapter, DEFAULT_OPP_CHANNEL);
75    }
76
77    public BluetoothOppRfcommListener(BluetoothAdapter adapter, int channel) {
78        mBtOppRfcommChannel = channel;
79        mAdapter = adapter;
80    }
81
82    public synchronized boolean start(Handler callback) {
83        if (mSocketAcceptThread == null) {
84            mCallback = callback;
85
86            mSocketAcceptThread = new Thread(TAG) {
87
88                public void run() {
89                    if (Constants.USE_TCP_DEBUG) {
90                        ServerSocket mServerSocket = null;
91                        try {
92                            if (V) Log.v(TAG, "Create ServerSocket on port "
93                                        + Constants.TCP_DEBUG_PORT);
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 (V) Log.v(TAG, "incomming connection time out");
108                                } else {
109                                    if (D) Log.d(TAG, "TCP Socket connected!");
110                                    Log.d(TAG, "remote addr is "
111                                            + clientSocket.getRemoteSocketAddress());
112                                    TestTcpTransport transport = new TestTcpTransport(clientSocket);
113                                    Message msg = Message.obtain();
114                                    msg.setTarget(mCallback);
115                                    msg.what = MSG_INCOMING_BTOPP_CONNECTION;
116                                    msg.obj = transport;
117                                    msg.sendToTarget();
118                                }
119                            } catch (SocketException e) {
120                                if (V) Log.v(TAG, "Error accept connection " + e);
121                            } catch (IOException e) {
122                                if (V) Log.v(TAG, "Error accept connection " + e);
123                            }
124                        }
125                        if (D) Log.d(TAG, "TCP listen thread finished");
126                        try {
127                            mServerSocket.close();
128                        } catch (IOException e) {
129                            Log.e(TAG, "Error close mServerSocker " + e);
130                        }
131                    } else {
132                        BluetoothServerSocket mServerSocket = null;
133                        boolean serverOK = true;
134
135                        /*
136                         * it's possible that create will fail in some cases.
137                         * retry for 10 times
138                         */
139                        for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) {
140                            try {
141                                mServerSocket =
142                                        mAdapter.listenUsingInsecureRfcommOn(mBtOppRfcommChannel);
143                            } catch (IOException e1) {
144                                Log.e(TAG, "Error create RfcommServerSocket " + e1);
145                                serverOK = false;
146                            }
147                            if (!serverOK) {
148                                synchronized (this) {
149                                    try {
150                                        if (V) Log.v(TAG, "wait 3 seconds");
151                                        Thread.sleep(3000);
152                                    } catch (InterruptedException e) {
153                                        Log.e(TAG, "socketAcceptThread thread was interrupted (3)");
154                                        mInterrupted = true;
155                                    }
156                                }
157                            } else {
158                                break;
159                            }
160                        }
161                        if (!serverOK) {
162                            Log.e(TAG, "Error start listening after " + CREATE_RETRY_TIME + " try");
163                            mInterrupted = true;
164                        }
165                        if (!mInterrupted) {
166                            Log.i(TAG, "Accept thread started on channel " + mBtOppRfcommChannel);
167                        }
168                        BluetoothSocket clientSocket;
169                        while (!mInterrupted) {
170                            try {
171                                clientSocket = mServerSocket.accept(ACCEPT_WAIT_TIMEOUT);
172                                Log.i(TAG, "Accepted connectoin from " + clientSocket.getRemoteDevice());
173                                BluetoothOppRfcommTransport transport = new BluetoothOppRfcommTransport(
174                                        clientSocket);
175                                Message msg = Message.obtain();
176                                msg.setTarget(mCallback);
177                                msg.what = MSG_INCOMING_BTOPP_CONNECTION;
178                                msg.obj = transport;
179                                msg.sendToTarget();
180                            } catch (IOException e) {
181                                //TODO later accept should not throw exception
182                                // if (V) Log.v(TAG, "Error accept connection " + e);
183                            }
184                        }
185                        try {
186                            if (mServerSocket != null) {
187                                if (V) Log.v(TAG, "close mServerSocket");
188                                mServerSocket.close();
189                            }
190                        } catch (IOException e) {
191                            Log.e(TAG, "Errro close mServerSocket " + e);
192                        }
193                        Log.i(TAG, "BluetoothSocket listen thread finished");
194                    }
195                }
196            };
197            mInterrupted = false;
198            mSocketAcceptThread.start();
199        }
200        return true;
201    }
202
203    public synchronized void stop() {
204        if (mSocketAcceptThread != null) {
205            Log.i(TAG, "stopping Accept Thread");
206
207            mInterrupted = true;
208            try {
209                mSocketAcceptThread.interrupt();
210                if (V) Log.v(TAG, "waiting for thread to terminate");
211                mSocketAcceptThread.join();
212                mSocketAcceptThread = null;
213                mCallback = null;
214            } catch (InterruptedException e) {
215                if (V) Log.v(TAG, "Interrupted waiting for Accept Thread to join");
216            }
217        }
218    }
219}
220