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