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.compat.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(publicAlternatives = "Use public {@link BluetoothServerSocket} API " 81 + "instead.") 82 /*package*/ final BluetoothSocket mSocket; 83 private Handler mHandler; 84 private int mMessage; 85 private int mChannel; 86 87 /** 88 * Construct a socket for incoming connections. 89 * 90 * @param type type of socket 91 * @param auth require the remote device to be authenticated 92 * @param encrypt require the connection to be encrypted 93 * @param port remote port 94 * @throws IOException On error, for example Bluetooth not available, or insufficient 95 * privileges 96 */ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port)97 /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port) 98 throws IOException { 99 mChannel = port; 100 mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null); 101 if (port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { 102 mSocket.setExcludeSdp(true); 103 } 104 } 105 106 /** 107 * Construct a socket for incoming connections. 108 * 109 * @param type type of socket 110 * @param auth require the remote device to be authenticated 111 * @param encrypt require the connection to be encrypted 112 * @param port remote port 113 * @param mitm enforce man-in-the-middle protection for authentication. 114 * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection 115 * @throws IOException On error, for example Bluetooth not available, or insufficient 116 * privileges 117 */ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port, boolean mitm, boolean min16DigitPin)118 /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port, 119 boolean mitm, boolean min16DigitPin) 120 throws IOException { 121 mChannel = port; 122 mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null, mitm, 123 min16DigitPin); 124 if (port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { 125 mSocket.setExcludeSdp(true); 126 } 127 } 128 129 /** 130 * Construct a socket for incoming connections. 131 * 132 * @param type type of socket 133 * @param auth require the remote device to be authenticated 134 * @param encrypt require the connection to be encrypted 135 * @param uuid uuid 136 * @throws IOException On error, for example Bluetooth not available, or insufficient 137 * privileges 138 */ BluetoothServerSocket(int type, boolean auth, boolean encrypt, ParcelUuid uuid)139 /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, ParcelUuid uuid) 140 throws IOException { 141 mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, -1, uuid); 142 // TODO: This is the same as mChannel = -1 - is this intentional? 143 mChannel = mSocket.getPort(); 144 } 145 146 147 /** 148 * Block until a connection is established. 149 * <p>Returns a connected {@link BluetoothSocket} on successful connection. 150 * <p>Once this call returns, it can be called again to accept subsequent 151 * incoming connections. 152 * <p>{@link #close} can be used to abort this call from another thread. 153 * 154 * @return a connected {@link BluetoothSocket} 155 * @throws IOException on error, for example this call was aborted, or timeout 156 */ accept()157 public BluetoothSocket accept() throws IOException { 158 return accept(-1); 159 } 160 161 /** 162 * Block until a connection is established, with timeout. 163 * <p>Returns a connected {@link BluetoothSocket} on successful connection. 164 * <p>Once this call returns, it can be called again to accept subsequent 165 * incoming connections. 166 * <p>{@link #close} can be used to abort this call from another thread. 167 * 168 * @return a connected {@link BluetoothSocket} 169 * @throws IOException on error, for example this call was aborted, or timeout 170 */ accept(int timeout)171 public BluetoothSocket accept(int timeout) throws IOException { 172 return mSocket.accept(timeout); 173 } 174 175 /** 176 * Immediately close this socket, and release all associated resources. 177 * <p>Causes blocked calls on this socket in other threads to immediately 178 * throw an IOException. 179 * <p>Closing the {@link BluetoothServerSocket} will <em>not</em> 180 * close any {@link BluetoothSocket} received from {@link #accept()}. 181 */ close()182 public void close() throws IOException { 183 if (DBG) Log.d(TAG, "BluetoothServerSocket:close() called. mChannel=" + mChannel); 184 synchronized (this) { 185 if (mHandler != null) { 186 mHandler.obtainMessage(mMessage).sendToTarget(); 187 } 188 } 189 mSocket.close(); 190 } 191 192 /*package*/ setCloseHandler(Handler handler, int message)193 synchronized void setCloseHandler(Handler handler, int message) { 194 mHandler = handler; 195 mMessage = message; 196 } 197 setServiceName(String serviceName)198 /*package*/ void setServiceName(String serviceName) { 199 mSocket.setServiceName(serviceName); 200 } 201 202 /** 203 * Returns the channel on which this socket is bound. 204 * 205 * @hide 206 */ getChannel()207 public int getChannel() { 208 return mChannel; 209 } 210 211 /** 212 * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP 213 * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the 214 * {@link BluetoothAdapter#listenUsingL2capChannel()} or {@link 215 * BluetoothAdapter#listenUsingInsecureL2capChannel()}. The returned value is undefined if this 216 * method is called on non-L2CAP server sockets. 217 * 218 * @return the assigned PSM or LE_PSM value depending on transport 219 */ getPsm()220 public int getPsm() { 221 return mChannel; 222 } 223 224 /** 225 * Sets the channel on which future sockets are bound. 226 * Currently used only when a channel is auto generated. 227 */ setChannel(int newChannel)228 /*package*/ void setChannel(int newChannel) { 229 /* TODO: From a design/architecture perspective this is wrong. 230 * The bind operation should be conducted through this class 231 * and the resulting port should be kept in mChannel, and 232 * not set from BluetoothAdapter. */ 233 if (mSocket != null) { 234 if (mSocket.getPort() != newChannel) { 235 Log.w(TAG, "The port set is different that the underlying port. mSocket.getPort(): " 236 + mSocket.getPort() + " requested newChannel: " + newChannel); 237 } 238 } 239 mChannel = newChannel; 240 } 241 242 @Override toString()243 public String toString() { 244 StringBuilder sb = new StringBuilder(); 245 sb.append("ServerSocket: Type: "); 246 switch (mSocket.getConnectionType()) { 247 case BluetoothSocket.TYPE_RFCOMM: { 248 sb.append("TYPE_RFCOMM"); 249 break; 250 } 251 case BluetoothSocket.TYPE_L2CAP: { 252 sb.append("TYPE_L2CAP"); 253 break; 254 } 255 case BluetoothSocket.TYPE_L2CAP_LE: { 256 sb.append("TYPE_L2CAP_LE"); 257 break; 258 } 259 case BluetoothSocket.TYPE_SCO: { 260 sb.append("TYPE_SCO"); 261 break; 262 } 263 } 264 sb.append(" Channel: ").append(mChannel); 265 return sb.toString(); 266 } 267 } 268