1 /*
2  * Copyright (C) 2009 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.UnsupportedAppUsage;
20 import android.os.Handler;
21 import android.os.ParcelUuid;
22 import android.util.Log;
23 
24 import java.io.Closeable;
25 import java.io.IOException;
26 
27 /**
28  * A listening Bluetooth socket.
29  *
30  * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:
31  * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
32  * side, use a {@link BluetoothServerSocket} to create a listening server
33  * socket. When a connection is accepted by the {@link BluetoothServerSocket},
34  * it will return a new {@link BluetoothSocket} to manage the connection.
35  * On the client side, use a single {@link BluetoothSocket} to both initiate
36  * an outgoing connection and to manage the connection.
37  *
38  * <p>For Bluetooth BR/EDR, the most common type of socket is RFCOMM, which is the type supported by
39  * the Android APIs. RFCOMM is a connection-oriented, streaming transport over Bluetooth BR/EDR. It
40  * is also known as the Serial Port Profile (SPP). To create a listening
41  * {@link BluetoothServerSocket} that's ready for incoming Bluetooth BR/EDR connections, use {@link
42  * BluetoothAdapter#listenUsingRfcommWithServiceRecord
43  * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}.
44  *
45  * <p>For Bluetooth LE, the socket uses LE Connection-oriented Channel (CoC). LE CoC is a
46  * connection-oriented, streaming transport over Bluetooth LE and has a credit-based flow control.
47  * Correspondingly, use {@link BluetoothAdapter#listenUsingL2capChannel
48  * BluetoothAdapter.listenUsingL2capChannel()} to create a listening {@link BluetoothServerSocket}
49  * that's ready for incoming Bluetooth LE CoC connections. For LE CoC, you can use {@link #getPsm()}
50  * to get the protocol/service multiplexer (PSM) value that the peer needs to use to connect to your
51  * socket.
52  *
53  * <p> After the listening {@link BluetoothServerSocket} is created, call {@link #accept()} to
54  * listen for incoming connection requests. This call will block until a connection is established,
55  * at which point, it will return a {@link BluetoothSocket} to manage the connection. Once the
56  * {@link BluetoothSocket} is acquired, it's a good idea to call {@link #close()} on the {@link
57  * BluetoothServerSocket} when it's no longer needed for accepting
58  * connections. Closing the {@link BluetoothServerSocket} will <em>not</em> close the returned
59  * {@link BluetoothSocket}.
60  *
61  * <p>{@link BluetoothServerSocket} is thread
62  * safe. In particular, {@link #close} will always immediately abort ongoing
63  * operations and close the server socket.
64  *
65  * <p class="note"><strong>Note:</strong>
66  * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
67  *
68  * <div class="special reference">
69  * <h3>Developer Guides</h3>
70  * <p>For more information about using Bluetooth, read the
71  * <a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer guide.</p>
72  * </div>
73  *
74  * {@see BluetoothSocket}
75  */
76 public final class BluetoothServerSocket implements Closeable {
77 
78     private static final String TAG = "BluetoothServerSocket";
79     private static final boolean DBG = false;
80     @UnsupportedAppUsage
81     /*package*/ final BluetoothSocket mSocket;
82     private Handler mHandler;
83     private int mMessage;
84     private int mChannel;
85 
86     /**
87      * Construct a socket for incoming connections.
88      *
89      * @param type type of socket
90      * @param auth require the remote device to be authenticated
91      * @param encrypt require the connection to be encrypted
92      * @param port remote port
93      * @throws IOException On error, for example Bluetooth not available, or insufficient
94      * privileges
95      */
BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port)96     /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port)
97             throws IOException {
98         mChannel = port;
99         mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null);
100         if (port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
101             mSocket.setExcludeSdp(true);
102         }
103     }
104 
105     /**
106      * Construct a socket for incoming connections.
107      *
108      * @param type type of socket
109      * @param auth require the remote device to be authenticated
110      * @param encrypt require the connection to be encrypted
111      * @param port remote port
112      * @param mitm enforce man-in-the-middle protection for authentication.
113      * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection
114      * @throws IOException On error, for example Bluetooth not available, or insufficient
115      * privileges
116      */
BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port, boolean mitm, boolean min16DigitPin)117     /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port,
118             boolean mitm, boolean min16DigitPin)
119             throws IOException {
120         mChannel = port;
121         mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null, mitm,
122                 min16DigitPin);
123         if (port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
124             mSocket.setExcludeSdp(true);
125         }
126     }
127 
128     /**
129      * Construct a socket for incoming connections.
130      *
131      * @param type type of socket
132      * @param auth require the remote device to be authenticated
133      * @param encrypt require the connection to be encrypted
134      * @param uuid uuid
135      * @throws IOException On error, for example Bluetooth not available, or insufficient
136      * privileges
137      */
BluetoothServerSocket(int type, boolean auth, boolean encrypt, ParcelUuid uuid)138     /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, ParcelUuid uuid)
139             throws IOException {
140         mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, -1, uuid);
141         // TODO: This is the same as mChannel = -1 - is this intentional?
142         mChannel = mSocket.getPort();
143     }
144 
145 
146     /**
147      * Block until a connection is established.
148      * <p>Returns a connected {@link BluetoothSocket} on successful connection.
149      * <p>Once this call returns, it can be called again to accept subsequent
150      * incoming connections.
151      * <p>{@link #close} can be used to abort this call from another thread.
152      *
153      * @return a connected {@link BluetoothSocket}
154      * @throws IOException on error, for example this call was aborted, or timeout
155      */
accept()156     public BluetoothSocket accept() throws IOException {
157         return accept(-1);
158     }
159 
160     /**
161      * Block until a connection is established, with timeout.
162      * <p>Returns a connected {@link BluetoothSocket} on successful connection.
163      * <p>Once this call returns, it can be called again to accept subsequent
164      * incoming connections.
165      * <p>{@link #close} can be used to abort this call from another thread.
166      *
167      * @return a connected {@link BluetoothSocket}
168      * @throws IOException on error, for example this call was aborted, or timeout
169      */
accept(int timeout)170     public BluetoothSocket accept(int timeout) throws IOException {
171         return mSocket.accept(timeout);
172     }
173 
174     /**
175      * Immediately close this socket, and release all associated resources.
176      * <p>Causes blocked calls on this socket in other threads to immediately
177      * throw an IOException.
178      * <p>Closing the {@link BluetoothServerSocket} will <em>not</em>
179      * close any {@link BluetoothSocket} received from {@link #accept()}.
180      */
close()181     public void close() throws IOException {
182         if (DBG) Log.d(TAG, "BluetoothServerSocket:close() called. mChannel=" + mChannel);
183         synchronized (this) {
184             if (mHandler != null) {
185                 mHandler.obtainMessage(mMessage).sendToTarget();
186             }
187         }
188         mSocket.close();
189     }
190 
191     /*package*/
setCloseHandler(Handler handler, int message)192     synchronized void setCloseHandler(Handler handler, int message) {
193         mHandler = handler;
194         mMessage = message;
195     }
196 
setServiceName(String serviceName)197     /*package*/ void setServiceName(String serviceName) {
198         mSocket.setServiceName(serviceName);
199     }
200 
201     /**
202      * Returns the channel on which this socket is bound.
203      *
204      * @hide
205      */
getChannel()206     public int getChannel() {
207         return mChannel;
208     }
209 
210     /**
211      * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP
212      * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the
213      * {@link BluetoothAdapter#listenUsingL2capChannel()} or {@link
214      * BluetoothAdapter#listenUsingInsecureL2capChannel()}. The returned value is undefined if this
215      * method is called on non-L2CAP server sockets.
216      *
217      * @return the assigned PSM or LE_PSM value depending on transport
218      */
getPsm()219     public int getPsm() {
220         return mChannel;
221     }
222 
223     /**
224      * Sets the channel on which future sockets are bound.
225      * Currently used only when a channel is auto generated.
226      */
setChannel(int newChannel)227     /*package*/ void setChannel(int newChannel) {
228         /* TODO: From a design/architecture perspective this is wrong.
229          *       The bind operation should be conducted through this class
230          *       and the resulting port should be kept in mChannel, and
231          *       not set from BluetoothAdapter. */
232         if (mSocket != null) {
233             if (mSocket.getPort() != newChannel) {
234                 Log.w(TAG, "The port set is different that the underlying port. mSocket.getPort(): "
235                         + mSocket.getPort() + " requested newChannel: " + newChannel);
236             }
237         }
238         mChannel = newChannel;
239     }
240 
241     @Override
toString()242     public String toString() {
243         StringBuilder sb = new StringBuilder();
244         sb.append("ServerSocket: Type: ");
245         switch (mSocket.getConnectionType()) {
246             case BluetoothSocket.TYPE_RFCOMM: {
247                 sb.append("TYPE_RFCOMM");
248                 break;
249             }
250             case BluetoothSocket.TYPE_L2CAP: {
251                 sb.append("TYPE_L2CAP");
252                 break;
253             }
254             case BluetoothSocket.TYPE_L2CAP_LE: {
255                 sb.append("TYPE_L2CAP_LE");
256                 break;
257             }
258             case BluetoothSocket.TYPE_SCO: {
259                 sb.append("TYPE_SCO");
260                 break;
261             }
262         }
263         sb.append(" Channel: ").append(mChannel);
264         return sb.toString();
265     }
266 }
267