blob: 540df737a6c9161020b1b5a926f79ea18d0abb18 [file] [log] [blame]
Nick Pelly53f441b2009-05-26 19:13:43 -07001/*
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
17package android.bluetooth;
18
Chen Chen874e7b02023-03-07 17:37:43 -080019import static android.bluetooth.BluetoothUtils.getSyncTimeout;
20
Jeff Sharkey5ba8bfc2021-04-16 09:53:23 -060021import android.annotation.SuppressLint;
Artur Satayevd8fe38c2019-12-10 17:47:52 +000022import android.compat.annotation.UnsupportedAppUsage;
Nick Pellyee1402d2009-10-02 20:34:18 -070023import android.os.Handler;
zzyfab62db2012-04-03 19:48:32 -070024import android.os.ParcelUuid;
Chen Chen874e7b02023-03-07 17:37:43 -080025import android.os.RemoteException;
Casper Bondec0a7c932015-04-09 09:24:48 +020026import android.util.Log;
Nick Pellyee1402d2009-10-02 20:34:18 -070027
Chen Chen874e7b02023-03-07 17:37:43 -080028import com.android.modules.utils.SynchronousResultReceiver;
29
Nick Pelly53f441b2009-05-26 19:13:43 -070030import java.io.Closeable;
31import java.io.IOException;
Chen Chen874e7b02023-03-07 17:37:43 -080032import java.util.concurrent.TimeoutException;
Nick Pelly53f441b2009-05-26 19:13:43 -070033
34/**
Nick Pelly753da532009-08-19 11:00:00 -070035 * A listening Bluetooth socket.
Nick Pelly53f441b2009-05-26 19:13:43 -070036 *
David Duarteee52b7e2023-12-02 01:32:11 +000037 * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets: {@link java.net.Socket}
38 * and {@link java.net.ServerSocket}. On the server side, use a {@link BluetoothServerSocket} to
39 * create a listening server socket. When a connection is accepted by the {@link
40 * BluetoothServerSocket}, it will return a new {@link BluetoothSocket} to manage the connection. On
41 * the client side, use a single {@link BluetoothSocket} to both initiate an outgoing connection and
42 * to manage the connection.
Nick Pelly53f441b2009-05-26 19:13:43 -070043 *
Stanley Tng3b285452019-04-19 14:27:09 -070044 * <p>For Bluetooth BR/EDR, the most common type of socket is RFCOMM, which is the type supported by
45 * the Android APIs. RFCOMM is a connection-oriented, streaming transport over Bluetooth BR/EDR. It
David Duarteee52b7e2023-12-02 01:32:11 +000046 * is also known as the Serial Port Profile (SPP). To create a listening {@link
47 * BluetoothServerSocket} that's ready for incoming Bluetooth BR/EDR connections, use {@link
Stanley Tng3b285452019-04-19 14:27:09 -070048 * BluetoothAdapter#listenUsingRfcommWithServiceRecord
49 * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}.
Nick Pelly53f441b2009-05-26 19:13:43 -070050 *
Stanley Tng3b285452019-04-19 14:27:09 -070051 * <p>For Bluetooth LE, the socket uses LE Connection-oriented Channel (CoC). LE CoC is a
52 * connection-oriented, streaming transport over Bluetooth LE and has a credit-based flow control.
53 * Correspondingly, use {@link BluetoothAdapter#listenUsingL2capChannel
54 * BluetoothAdapter.listenUsingL2capChannel()} to create a listening {@link BluetoothServerSocket}
55 * that's ready for incoming Bluetooth LE CoC connections. For LE CoC, you can use {@link #getPsm()}
56 * to get the protocol/service multiplexer (PSM) value that the peer needs to use to connect to your
57 * socket.
58 *
David Duarteee52b7e2023-12-02 01:32:11 +000059 * <p>After the listening {@link BluetoothServerSocket} is created, call {@link #accept()} to listen
60 * for incoming connection requests. This call will block until a connection is established, at
61 * which point, it will return a {@link BluetoothSocket} to manage the connection. Once the {@link
62 * BluetoothSocket} is acquired, it's a good idea to call {@link #close()} on the {@link
63 * BluetoothServerSocket} when it's no longer needed for accepting connections. Closing the {@link
64 * BluetoothServerSocket} will <em>not</em> close the returned {@link BluetoothSocket}.
Nick Pelly753da532009-08-19 11:00:00 -070065 *
David Duarteee52b7e2023-12-02 01:32:11 +000066 * <p>{@link BluetoothServerSocket} is thread safe. In particular, {@link #close} will always
67 * immediately abort ongoing operations and close the server socket.
Nick Pellyed6f2ce2009-09-08 10:12:06 -070068 *
David Duarteee52b7e2023-12-02 01:32:11 +000069 * <p><div class="special reference">
70 *
Joe Fernandezda4e2ab2011-12-20 10:38:34 -080071 * <h3>Developer Guides</h3>
Joe Fernandezda4e2ab2011-12-20 10:38:34 -080072 *
David Duarteee52b7e2023-12-02 01:32:11 +000073 * <p>For more information about using Bluetooth, read the <a
74 * href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer guide. </div>
75 *
David Duarte5a02bb42023-12-04 23:07:42 +000076 * @see BluetoothSocket
Nick Pelly53f441b2009-05-26 19:13:43 -070077 */
Jeff Sharkey5ba8bfc2021-04-16 09:53:23 -060078@SuppressLint("AndroidFrameworkBluetoothPermission")
Nick Pelly53f441b2009-05-26 19:13:43 -070079public final class BluetoothServerSocket implements Closeable {
Nick Pelly4cd2cd92009-09-02 11:51:35 -070080
Casper Bondec0a7c932015-04-09 09:24:48 +020081 private static final String TAG = "BluetoothServerSocket";
Ugo Yub6dc15a2022-12-28 20:18:00 +080082 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
David Duarteee52b7e2023-12-02 01:32:11 +000083
84 @UnsupportedAppUsage(
85 publicAlternatives = "Use public {@link BluetoothServerSocket} API " + "instead.")
Nick Pelly2d664882009-08-14 18:33:38 -070086 /*package*/ final BluetoothSocket mSocket;
David Duarteee52b7e2023-12-02 01:32:11 +000087
Nick Pellyee1402d2009-10-02 20:34:18 -070088 private Handler mHandler;
89 private int mMessage;
Casper Bondec0a7c932015-04-09 09:24:48 +020090 private int mChannel;
Chen Chen61174be2023-03-16 17:42:36 -070091 private long mSocketCreationTimeMillis = 0;
92 private long mSocketCreationLatencyMillis = 0;
Chen Chen874e7b02023-03-07 17:37:43 -080093
94 // BluetoothSocket.getConnectionType() will hide L2CAP_LE.
95 // Therefore a new variable need to be maintained here.
96 private int mType;
Nick Pelly53f441b2009-05-26 19:13:43 -070097
98 /**
99 * Construct a socket for incoming connections.
Jack He910201b2017-08-22 16:06:54 -0700100 *
101 * @param type type of socket
102 * @param auth require the remote device to be authenticated
Nick Pellycb32d7c2009-06-02 15:57:18 -0700103 * @param encrypt require the connection to be encrypted
Jack He910201b2017-08-22 16:06:54 -0700104 * @param port remote port
David Duarteee52b7e2023-12-02 01:32:11 +0000105 * @throws IOException On error, for example Bluetooth not available, or insufficient privileges
Nick Pelly53f441b2009-05-26 19:13:43 -0700106 */
Nick Pelly2d664882009-08-14 18:33:38 -0700107 /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port)
Nick Pellycb32d7c2009-06-02 15:57:18 -0700108 throws IOException {
Chen Chen61174be2023-03-16 17:42:36 -0700109 mSocketCreationTimeMillis = System.currentTimeMillis();
Chen Chen874e7b02023-03-07 17:37:43 -0800110 mType = type;
Ben Dodson48d0cca2011-07-08 14:36:42 -0700111 mChannel = port;
David Duarte5a02bb42023-12-04 23:07:42 +0000112 mSocket = new BluetoothSocket(type, auth, encrypt, null, port, null);
Casper Bonde60d77c22015-04-21 13:12:05 +0200113 if (port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
114 mSocket.setExcludeSdp(true);
115 }
Chen Chen61174be2023-03-16 17:42:36 -0700116 mSocketCreationLatencyMillis = System.currentTimeMillis() - mSocketCreationTimeMillis;
Casper Bonde60d77c22015-04-21 13:12:05 +0200117 }
118
119 /**
120 * Construct a socket for incoming connections.
Jack He910201b2017-08-22 16:06:54 -0700121 *
122 * @param type type of socket
123 * @param auth require the remote device to be authenticated
Casper Bonde60d77c22015-04-21 13:12:05 +0200124 * @param encrypt require the connection to be encrypted
Jack He910201b2017-08-22 16:06:54 -0700125 * @param port remote port
Jeff Sharkeyc5386af2020-09-11 14:57:21 -0600126 * @param mitm enforce person-in-the-middle protection for authentication.
Casper Bonde91276012015-05-08 14:32:24 +0200127 * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection
David Duarteee52b7e2023-12-02 01:32:11 +0000128 * @throws IOException On error, for example Bluetooth not available, or insufficient privileges
Casper Bonde60d77c22015-04-21 13:12:05 +0200129 */
David Duarteee52b7e2023-12-02 01:32:11 +0000130 /*package*/ BluetoothServerSocket(
131 int type, boolean auth, boolean encrypt, int port, boolean mitm, boolean min16DigitPin)
Casper Bonde60d77c22015-04-21 13:12:05 +0200132 throws IOException {
Chen Chen61174be2023-03-16 17:42:36 -0700133 mSocketCreationTimeMillis = System.currentTimeMillis();
Chen Chen874e7b02023-03-07 17:37:43 -0800134 mType = type;
Casper Bonde60d77c22015-04-21 13:12:05 +0200135 mChannel = port;
David Duarte5a02bb42023-12-04 23:07:42 +0000136 mSocket = new BluetoothSocket(type, auth, encrypt, null, port, null, mitm, min16DigitPin);
Jack He910201b2017-08-22 16:06:54 -0700137 if (port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
Casper Bondec0a7c932015-04-09 09:24:48 +0200138 mSocket.setExcludeSdp(true);
139 }
Chen Chen61174be2023-03-16 17:42:36 -0700140 mSocketCreationLatencyMillis = System.currentTimeMillis() - mSocketCreationTimeMillis;
Nick Pelly53f441b2009-05-26 19:13:43 -0700141 }
142
143 /**
zzyfab62db2012-04-03 19:48:32 -0700144 * Construct a socket for incoming connections.
Jack He910201b2017-08-22 16:06:54 -0700145 *
146 * @param type type of socket
147 * @param auth require the remote device to be authenticated
zzyfab62db2012-04-03 19:48:32 -0700148 * @param encrypt require the connection to be encrypted
Jack He910201b2017-08-22 16:06:54 -0700149 * @param uuid uuid
David Duarteee52b7e2023-12-02 01:32:11 +0000150 * @throws IOException On error, for example Bluetooth not available, or insufficient privileges
zzyfab62db2012-04-03 19:48:32 -0700151 */
152 /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, ParcelUuid uuid)
153 throws IOException {
Chen Chen61174be2023-03-16 17:42:36 -0700154 mSocketCreationTimeMillis = System.currentTimeMillis();
Chen Chen874e7b02023-03-07 17:37:43 -0800155 mType = type;
David Duarte5a02bb42023-12-04 23:07:42 +0000156 mSocket = new BluetoothSocket(type, auth, encrypt, null, -1, uuid);
Casper Bondec0a7c932015-04-09 09:24:48 +0200157 // TODO: This is the same as mChannel = -1 - is this intentional?
zzyfab62db2012-04-03 19:48:32 -0700158 mChannel = mSocket.getPort();
Chen Chen61174be2023-03-16 17:42:36 -0700159 mSocketCreationLatencyMillis = System.currentTimeMillis() - mSocketCreationTimeMillis;
zzyfab62db2012-04-03 19:48:32 -0700160 }
161
zzyfab62db2012-04-03 19:48:32 -0700162 /**
Nick Pelly53f441b2009-05-26 19:13:43 -0700163 * Block until a connection is established.
David Duarteee52b7e2023-12-02 01:32:11 +0000164 *
Nick Pelly753da532009-08-19 11:00:00 -0700165 * <p>Returns a connected {@link BluetoothSocket} on successful connection.
David Duarteee52b7e2023-12-02 01:32:11 +0000166 *
167 * <p>Once this call returns, it can be called again to accept subsequent incoming connections.
168 *
Nick Pelly753da532009-08-19 11:00:00 -0700169 * <p>{@link #close} can be used to abort this call from another thread.
Jack He910201b2017-08-22 16:06:54 -0700170 *
Nick Pelly753da532009-08-19 11:00:00 -0700171 * @return a connected {@link BluetoothSocket}
Jack He910201b2017-08-22 16:06:54 -0700172 * @throws IOException on error, for example this call was aborted, or timeout
Nick Pelly53f441b2009-05-26 19:13:43 -0700173 */
174 public BluetoothSocket accept() throws IOException {
175 return accept(-1);
176 }
177
178 /**
179 * Block until a connection is established, with timeout.
David Duarteee52b7e2023-12-02 01:32:11 +0000180 *
Nick Pelly753da532009-08-19 11:00:00 -0700181 * <p>Returns a connected {@link BluetoothSocket} on successful connection.
David Duarteee52b7e2023-12-02 01:32:11 +0000182 *
183 * <p>Once this call returns, it can be called again to accept subsequent incoming connections.
184 *
Nick Pelly753da532009-08-19 11:00:00 -0700185 * <p>{@link #close} can be used to abort this call from another thread.
Jack He910201b2017-08-22 16:06:54 -0700186 *
Nick Pelly753da532009-08-19 11:00:00 -0700187 * @return a connected {@link BluetoothSocket}
Jack He910201b2017-08-22 16:06:54 -0700188 * @throws IOException on error, for example this call was aborted, or timeout
Nick Pelly53f441b2009-05-26 19:13:43 -0700189 */
190 public BluetoothSocket accept(int timeout) throws IOException {
Chen Chen61174be2023-03-16 17:42:36 -0700191 long socketConnectionTime = System.currentTimeMillis();
Chen Chen874e7b02023-03-07 17:37:43 -0800192 BluetoothSocket acceptedSocket = null;
193 try {
194 acceptedSocket = mSocket.accept(timeout);
195 logL2capcocServerConnection(
Chen Chen61174be2023-03-16 17:42:36 -0700196 acceptedSocket,
197 timeout,
198 BluetoothSocket.RESULT_L2CAP_CONN_SUCCESS,
199 socketConnectionTime);
Chen Chen874e7b02023-03-07 17:37:43 -0800200 return acceptedSocket;
201 } catch (IOException e) {
202 logL2capcocServerConnection(
Chen Chen61174be2023-03-16 17:42:36 -0700203 acceptedSocket,
204 timeout,
205 BluetoothSocket.RESULT_L2CAP_CONN_SERVER_FAILURE,
206 socketConnectionTime);
Chen Chen874e7b02023-03-07 17:37:43 -0800207 throw e;
208 }
Nick Pelly53f441b2009-05-26 19:13:43 -0700209 }
210
Chen Chen874e7b02023-03-07 17:37:43 -0800211 private void logL2capcocServerConnection(
Chen Chen61174be2023-03-16 17:42:36 -0700212 BluetoothSocket acceptedSocket,
213 int timeout,
214 int result,
215 long socketConnectionTimeMillis) {
Chen Chen874e7b02023-03-07 17:37:43 -0800216 if (mType != BluetoothSocket.TYPE_L2CAP_LE) {
217 return;
218 }
David Duarteee52b7e2023-12-02 01:32:11 +0000219 IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService();
Chen Chen874e7b02023-03-07 17:37:43 -0800220 if (bluetoothProxy == null) {
David Duarteee52b7e2023-12-02 01:32:11 +0000221 Log.w(TAG, "bluetoothProxy is null while trying to log l2cap soc server connection");
Chen Chen874e7b02023-03-07 17:37:43 -0800222 return;
223 }
224 try {
225 final SynchronousResultReceiver recv = SynchronousResultReceiver.get();
226 bluetoothProxy.logL2capcocServerConnection(
227 acceptedSocket == null ? null : acceptedSocket.getRemoteDevice(),
228 getPsm(),
229 mSocket.isAuth(),
230 result,
Chen Chen61174be2023-03-16 17:42:36 -0700231 mSocketCreationTimeMillis, // pass creation time to calculate end to end latency
232 mSocketCreationLatencyMillis, // socket creation latency
233 socketConnectionTimeMillis, // send connection start time for connection latency
Chen Chen874e7b02023-03-07 17:37:43 -0800234 timeout,
235 recv);
236 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
237
238 } catch (RemoteException | TimeoutException e) {
239 Log.w(TAG, "logL2capcocServerConnection failed due to remote exception");
240 }
241 }
David Duarteee52b7e2023-12-02 01:32:11 +0000242
Nick Pelly53f441b2009-05-26 19:13:43 -0700243 /**
Nick Pelly753da532009-08-19 11:00:00 -0700244 * Immediately close this socket, and release all associated resources.
David Duarteee52b7e2023-12-02 01:32:11 +0000245 *
246 * <p>Causes blocked calls on this socket in other threads to immediately throw an IOException.
247 *
248 * <p>Closing the {@link BluetoothServerSocket} will <em>not</em> close any {@link
249 * BluetoothSocket} received from {@link #accept()}.
Nick Pelly53f441b2009-05-26 19:13:43 -0700250 */
251 public void close() throws IOException {
Stanley Tngd67d5e42017-11-22 16:04:40 -0800252 if (DBG) Log.d(TAG, "BluetoothServerSocket:close() called. mChannel=" + mChannel);
Nick Pellyee1402d2009-10-02 20:34:18 -0700253 synchronized (this) {
254 if (mHandler != null) {
255 mHandler.obtainMessage(mMessage).sendToTarget();
256 }
257 }
Nick Pelly4cd2cd92009-09-02 11:51:35 -0700258 mSocket.close();
Nick Pelly53f441b2009-05-26 19:13:43 -0700259 }
Nick Pellyee1402d2009-10-02 20:34:18 -0700260
Jack He910201b2017-08-22 16:06:54 -0700261 /*package*/
262 synchronized void setCloseHandler(Handler handler, int message) {
Nick Pellyee1402d2009-10-02 20:34:18 -0700263 mHandler = handler;
264 mMessage = message;
265 }
Jack He910201b2017-08-22 16:06:54 -0700266
Jack He9e045d22017-08-22 21:21:23 -0700267 /*package*/ void setServiceName(String serviceName) {
268 mSocket.setServiceName(serviceName);
zzyfab62db2012-04-03 19:48:32 -0700269 }
Casper Bondec0a7c932015-04-09 09:24:48 +0200270
Ben Dodson48d0cca2011-07-08 14:36:42 -0700271 /**
272 * Returns the channel on which this socket is bound.
Jack He910201b2017-08-22 16:06:54 -0700273 *
Ben Dodson48d0cca2011-07-08 14:36:42 -0700274 * @hide
275 */
276 public int getChannel() {
277 return mChannel;
278 }
Casper Bondec0a7c932015-04-09 09:24:48 +0200279
280 /**
Stanley Tngd67d5e42017-11-22 16:04:40 -0800281 * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP
282 * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the
Xin Li10802fb2019-02-13 22:36:25 -0800283 * {@link BluetoothAdapter#listenUsingL2capChannel()} or {@link
284 * BluetoothAdapter#listenUsingInsecureL2capChannel()}. The returned value is undefined if this
Stanley Tngd67d5e42017-11-22 16:04:40 -0800285 * method is called on non-L2CAP server sockets.
286 *
287 * @return the assigned PSM or LE_PSM value depending on transport
Stanley Tngd67d5e42017-11-22 16:04:40 -0800288 */
289 public int getPsm() {
290 return mChannel;
291 }
292
293 /**
David Duarteee52b7e2023-12-02 01:32:11 +0000294 * Sets the channel on which future sockets are bound. Currently used only when a channel is
295 * auto generated.
Casper Bondec0a7c932015-04-09 09:24:48 +0200296 */
297 /*package*/ void setChannel(int newChannel) {
298 /* TODO: From a design/architecture perspective this is wrong.
299 * The bind operation should be conducted through this class
300 * and the resulting port should be kept in mChannel, and
301 * not set from BluetoothAdapter. */
Jack He910201b2017-08-22 16:06:54 -0700302 if (mSocket != null) {
303 if (mSocket.getPort() != newChannel) {
David Duarteee52b7e2023-12-02 01:32:11 +0000304 Log.w(
305 TAG,
306 "The port set is different that the underlying port. mSocket.getPort(): "
307 + mSocket.getPort()
308 + " requested newChannel: "
309 + newChannel);
Casper Bondec0a7c932015-04-09 09:24:48 +0200310 }
311 }
312 mChannel = newChannel;
313 }
314
315 @Override
316 public String toString() {
317 StringBuilder sb = new StringBuilder();
318 sb.append("ServerSocket: Type: ");
Jack He910201b2017-08-22 16:06:54 -0700319 switch (mSocket.getConnectionType()) {
David Duarteee52b7e2023-12-02 01:32:11 +0000320 case BluetoothSocket.TYPE_RFCOMM:
321 {
322 sb.append("TYPE_RFCOMM");
323 break;
324 }
325 case BluetoothSocket.TYPE_L2CAP:
326 {
327 sb.append("TYPE_L2CAP");
328 break;
329 }
330 case BluetoothSocket.TYPE_L2CAP_LE:
331 {
332 sb.append("TYPE_L2CAP_LE");
333 break;
334 }
335 case BluetoothSocket.TYPE_SCO:
336 {
337 sb.append("TYPE_SCO");
338 break;
339 }
Casper Bondec0a7c932015-04-09 09:24:48 +0200340 }
341 sb.append(" Channel: ").append(mChannel);
342 return sb.toString();
343 }
Nick Pelly53f441b2009-05-26 19:13:43 -0700344}