blob: 98a5341b30257cd5b85e408b37aff8386e854694 [file] [log] [blame]
Nick Pelly0b6955a2009-05-26 19:13:43 -07001/*
Zhihai Xufa0fd392012-10-23 17:31:56 -07002 * Copyright (C) 2012 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.
Nick Pelly0b6955a2009-05-26 19:13:43 -070015 */
16
17package android.bluetooth;
18
Nick Pelly16fb88a2009-10-07 07:44:03 +020019import android.os.ParcelUuid;
zzy3b147b72012-04-03 19:48:32 -070020import android.os.ParcelFileDescriptor;
Nick Pelly16fb88a2009-10-07 07:44:03 +020021import android.os.RemoteException;
22import android.util.Log;
23
Casper Bonde238e0f92015-04-09 09:24:48 +020024import java.io.BufferedInputStream;
Nick Pelly0b6955a2009-05-26 19:13:43 -070025import java.io.Closeable;
zzy3b147b72012-04-03 19:48:32 -070026import java.io.FileDescriptor;
Nick Pelly0b6955a2009-05-26 19:13:43 -070027import java.io.IOException;
28import java.io.InputStream;
29import java.io.OutputStream;
Andreas Gampee6748ce2015-12-11 18:00:38 -080030import java.util.Arrays;
Jeff Sharkeyfea17de2013-06-11 14:13:09 -070031import java.util.Locale;
zzyb49a8962012-10-11 14:52:43 -070032import java.util.UUID;
zzy3b147b72012-04-03 19:48:32 -070033import android.net.LocalSocket;
Casper Bonde238e0f92015-04-09 09:24:48 +020034
35import java.nio.Buffer;
zzy3b147b72012-04-03 19:48:32 -070036import java.nio.ByteOrder;
37import java.nio.ByteBuffer;
Nick Pelly0b6955a2009-05-26 19:13:43 -070038/**
Nick Pelly45e27042009-08-19 11:00:00 -070039 * A connected or connecting Bluetooth socket.
Nick Pelly0b6955a2009-05-26 19:13:43 -070040 *
Nick Pelly45e27042009-08-19 11:00:00 -070041 * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:
42 * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
43 * side, use a {@link BluetoothServerSocket} to create a listening server
Scott Main9fab0ae2009-11-03 18:17:59 -080044 * socket. When a connection is accepted by the {@link BluetoothServerSocket},
45 * it will return a new {@link BluetoothSocket} to manage the connection.
Jake Hambyf51eada2010-09-21 13:39:53 -070046 * On the client side, use a single {@link BluetoothSocket} to both initiate
Scott Main9fab0ae2009-11-03 18:17:59 -080047 * an outgoing connection and to manage the connection.
Nick Pelly0b6955a2009-05-26 19:13:43 -070048 *
Scott Main9fab0ae2009-11-03 18:17:59 -080049 * <p>The most common type of Bluetooth socket is RFCOMM, which is the type
50 * supported by the Android APIs. RFCOMM is a connection-oriented, streaming
51 * transport over Bluetooth. It is also known as the Serial Port Profile (SPP).
Nick Pelly0b6955a2009-05-26 19:13:43 -070052 *
Scott Main9fab0ae2009-11-03 18:17:59 -080053 * <p>To create a {@link BluetoothSocket} for connecting to a known device, use
54 * {@link BluetoothDevice#createRfcommSocketToServiceRecord
55 * BluetoothDevice.createRfcommSocketToServiceRecord()}.
56 * Then call {@link #connect()} to attempt a connection to the remote device.
57 * This call will block until a connection is established or the connection
58 * fails.
Nick Pelly45e27042009-08-19 11:00:00 -070059 *
Scott Main9fab0ae2009-11-03 18:17:59 -080060 * <p>To create a {@link BluetoothSocket} as a server (or "host"), see the
61 * {@link BluetoothServerSocket} documentation.
Nick Pelly45e27042009-08-19 11:00:00 -070062 *
Scott Main9fab0ae2009-11-03 18:17:59 -080063 * <p>Once the socket is connected, whether initiated as a client or accepted
64 * as a server, open the IO streams by calling {@link #getInputStream} and
65 * {@link #getOutputStream} in order to retrieve {@link java.io.InputStream}
66 * and {@link java.io.OutputStream} objects, respectively, which are
67 * automatically connected to the socket.
68 *
69 * <p>{@link BluetoothSocket} is thread
Nick Pelly45e27042009-08-19 11:00:00 -070070 * safe. In particular, {@link #close} will always immediately abort ongoing
71 * operations and close the socket.
Nick Pellycf440592009-09-08 10:12:06 -070072 *
Scott Main9fab0ae2009-11-03 18:17:59 -080073 * <p class="note"><strong>Note:</strong>
74 * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
75 *
Joe Fernandez3aef8e1d2011-12-20 10:38:34 -080076 * <div class="special reference">
77 * <h3>Developer Guides</h3>
78 * <p>For more information about using Bluetooth, read the
Marie Janssen382871b2016-06-20 10:26:31 -070079 * <a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer guide.</p>
Joe Fernandez3aef8e1d2011-12-20 10:38:34 -080080 * </div>
81 *
Scott Main9fab0ae2009-11-03 18:17:59 -080082 * {@see BluetoothServerSocket}
83 * {@see java.io.InputStream}
84 * {@see java.io.OutputStream}
Nick Pelly0b6955a2009-05-26 19:13:43 -070085 */
86public final class BluetoothSocket implements Closeable {
Nick Pelly16fb88a2009-10-07 07:44:03 +020087 private static final String TAG = "BluetoothSocket";
Joe LaPennaf3de98a2014-05-13 18:17:46 -070088 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
89 private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
Nick Pelly16fb88a2009-10-07 07:44:03 +020090
Nick Pelly24bb9b82009-10-02 20:34:18 -070091 /** @hide */
92 public static final int MAX_RFCOMM_CHANNEL = 30;
Casper Bonde238e0f92015-04-09 09:24:48 +020093 /*package*/ static final int MAX_L2CAP_PACKAGE_SIZE = 0xFFFF;
Nick Pelly24bb9b82009-10-02 20:34:18 -070094
Andre Eisenbachf4559862015-05-04 13:48:50 -070095 /** RFCOMM socket */
Casper Bonde238e0f92015-04-09 09:24:48 +020096 public static final int TYPE_RFCOMM = 1;
Andre Eisenbachf4559862015-05-04 13:48:50 -070097
98 /** SCO socket */
Casper Bonde238e0f92015-04-09 09:24:48 +020099 public static final int TYPE_SCO = 2;
Andre Eisenbachf4559862015-05-04 13:48:50 -0700100
101 /** L2CAP socket */
Casper Bonde238e0f92015-04-09 09:24:48 +0200102 public static final int TYPE_L2CAP = 3;
Nick Pelly6a669fa2009-06-02 15:57:18 -0700103
Nick Pelly24bb9b82009-10-02 20:34:18 -0700104 /*package*/ static final int EBADFD = 77;
105 /*package*/ static final int EADDRINUSE = 98;
106
zzy3b147b72012-04-03 19:48:32 -0700107 /*package*/ static final int SEC_FLAG_ENCRYPT = 1;
108 /*package*/ static final int SEC_FLAG_AUTH = 1 << 1;
Casper Bonde238e0f92015-04-09 09:24:48 +0200109 /*package*/ static final int BTSOCK_FLAG_NO_SDP = 1 << 2;
Casper Bonde23284232015-04-21 13:12:05 +0200110 /*package*/ static final int SEC_FLAG_AUTH_MITM = 1 << 3;
Casper Bonde3b3d1fe2015-05-08 14:32:24 +0200111 /*package*/ static final int SEC_FLAG_AUTH_16_DIGIT = 1 << 4;
zzy3b147b72012-04-03 19:48:32 -0700112
Nick Pelly6a669fa2009-06-02 15:57:18 -0700113 private final int mType; /* one of TYPE_RFCOMM etc */
zzy3b147b72012-04-03 19:48:32 -0700114 private BluetoothDevice mDevice; /* remote device */
115 private String mAddress; /* remote address */
Nick Pelly0b6955a2009-05-26 19:13:43 -0700116 private final boolean mAuth;
117 private final boolean mEncrypt;
118 private final BluetoothInputStream mInputStream;
119 private final BluetoothOutputStream mOutputStream;
zzy3b147b72012-04-03 19:48:32 -0700120 private final ParcelUuid mUuid;
Casper Bonde23284232015-04-21 13:12:05 +0200121 private boolean mExcludeSdp = false; /* when true no SPP SDP record will be created */
122 private boolean mAuthMitm = false; /* when true Man-in-the-middle protection will be enabled*/
Casper Bonde3b3d1fe2015-05-08 14:32:24 +0200123 private boolean mMin16DigitPin = false; /* Minimum 16 digit pin for sec mode 2 connections */
zzy3b147b72012-04-03 19:48:32 -0700124 private ParcelFileDescriptor mPfd;
125 private LocalSocket mSocket;
126 private InputStream mSocketIS;
127 private OutputStream mSocketOS;
Mike Lockwood68c692d2013-10-02 07:56:46 -0700128 private int mPort; /* RFCOMM channel or L2CAP psm */
zzy3b147b72012-04-03 19:48:32 -0700129 private int mFd;
130 private String mServiceName;
zzy3b147b72012-04-03 19:48:32 -0700131 private static int PROXY_CONNECTION_TIMEOUT = 5000;
132
Casper Bonde238e0f92015-04-09 09:24:48 +0200133 private static int SOCK_SIGNAL_SIZE = 20;
134
135 private ByteBuffer mL2capBuffer = null;
136 private int mMaxTxPacketSize = 0; // The l2cap maximum packet size supported by the peer.
137 private int mMaxRxPacketSize = 0; // The l2cap maximum packet size that can be received.
Nick Pelly0b6955a2009-05-26 19:13:43 -0700138
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700139 private enum SocketState {
140 INIT,
141 CONNECTED,
zzy3b147b72012-04-03 19:48:32 -0700142 LISTENING,
143 CLOSED,
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700144 }
Nick Pelly71c3c782009-09-02 11:51:35 -0700145
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700146 /** prevents all native calls after destroyNative() */
zzy3b147b72012-04-03 19:48:32 -0700147 private volatile SocketState mSocketState;
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700148
149 /** protects mSocketState */
zzy3b147b72012-04-03 19:48:32 -0700150 //private final ReentrantReadWriteLock mLock;
Nick Pelly0b6955a2009-05-26 19:13:43 -0700151
152 /**
Nick Pellybd022f42009-08-14 18:33:38 -0700153 * Construct a BluetoothSocket.
Nick Pelly6a669fa2009-06-02 15:57:18 -0700154 * @param type type of socket
Nick Pelly0b6955a2009-05-26 19:13:43 -0700155 * @param fd fd to use for connected socket, or -1 for a new socket
156 * @param auth require the remote device to be authenticated
157 * @param encrypt require the connection to be encrypted
Nick Pellybd022f42009-08-14 18:33:38 -0700158 * @param device remote device that this socket can connect to
Nick Pelly0b6955a2009-05-26 19:13:43 -0700159 * @param port remote port
Nick Pelly16fb88a2009-10-07 07:44:03 +0200160 * @param uuid SDP uuid
Nick Pelly0b6955a2009-05-26 19:13:43 -0700161 * @throws IOException On error, for example Bluetooth not available, or
Jake Hambyf51eada2010-09-21 13:39:53 -0700162 * insufficient privileges
Nick Pelly0b6955a2009-05-26 19:13:43 -0700163 */
Nick Pellybd022f42009-08-14 18:33:38 -0700164 /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
Nick Pelly16fb88a2009-10-07 07:44:03 +0200165 BluetoothDevice device, int port, ParcelUuid uuid) throws IOException {
Casper Bonde3b3d1fe2015-05-08 14:32:24 +0200166 this(type, fd, auth, encrypt, device, port, uuid, false, false);
Casper Bonde23284232015-04-21 13:12:05 +0200167 }
168
169 /**
170 * Construct a BluetoothSocket.
171 * @param type type of socket
172 * @param fd fd to use for connected socket, or -1 for a new socket
173 * @param auth require the remote device to be authenticated
174 * @param encrypt require the connection to be encrypted
175 * @param device remote device that this socket can connect to
176 * @param port remote port
177 * @param uuid SDP uuid
178 * @param mitm enforce man-in-the-middle protection.
Casper Bonde3b3d1fe2015-05-08 14:32:24 +0200179 * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection
Casper Bonde23284232015-04-21 13:12:05 +0200180 * @throws IOException On error, for example Bluetooth not available, or
181 * insufficient privileges
182 */
183 /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
Casper Bonde3b3d1fe2015-05-08 14:32:24 +0200184 BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm, boolean min16DigitPin)
185 throws IOException {
Casper Bonde238e0f92015-04-09 09:24:48 +0200186 if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type);
187 if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1
188 && port != BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
Nick Pelly24bb9b82009-10-02 20:34:18 -0700189 if (port < 1 || port > MAX_RFCOMM_CHANNEL) {
190 throw new IOException("Invalid RFCOMM channel: " + port);
191 }
192 }
Casper Bonde238e0f92015-04-09 09:24:48 +0200193 if (uuid != null)
zzyb49a8962012-10-11 14:52:43 -0700194 mUuid = uuid;
195 else mUuid = new ParcelUuid(new UUID(0, 0));
Nick Pelly6a669fa2009-06-02 15:57:18 -0700196 mType = type;
Nick Pelly0b6955a2009-05-26 19:13:43 -0700197 mAuth = auth;
Casper Bonde23284232015-04-21 13:12:05 +0200198 mAuthMitm = mitm;
Casper Bonde3b3d1fe2015-05-08 14:32:24 +0200199 mMin16DigitPin = min16DigitPin;
Nick Pelly0b6955a2009-05-26 19:13:43 -0700200 mEncrypt = encrypt;
Nick Pellybd022f42009-08-14 18:33:38 -0700201 mDevice = device;
zzy3b147b72012-04-03 19:48:32 -0700202 mPort = port;
203 mFd = fd;
204
205 mSocketState = SocketState.INIT;
206
Nick Pellybd022f42009-08-14 18:33:38 -0700207 if (device == null) {
zzy3b147b72012-04-03 19:48:32 -0700208 // Server socket
209 mAddress = BluetoothAdapter.getDefaultAdapter().getAddress();
Nick Pellybd022f42009-08-14 18:33:38 -0700210 } else {
zzy3b147b72012-04-03 19:48:32 -0700211 // Remote socket
Nick Pellybd022f42009-08-14 18:33:38 -0700212 mAddress = device.getAddress();
213 }
Nick Pelly0b6955a2009-05-26 19:13:43 -0700214 mInputStream = new BluetoothInputStream(this);
215 mOutputStream = new BluetoothOutputStream(this);
zzy3b147b72012-04-03 19:48:32 -0700216 }
217 private BluetoothSocket(BluetoothSocket s) {
Casper Bonde238e0f92015-04-09 09:24:48 +0200218 if (VDBG) Log.d(TAG, "Creating new Private BluetoothSocket of type: " + s.mType);
zzy3b147b72012-04-03 19:48:32 -0700219 mUuid = s.mUuid;
220 mType = s.mType;
221 mAuth = s.mAuth;
222 mEncrypt = s.mEncrypt;
223 mPort = s.mPort;
224 mInputStream = new BluetoothInputStream(this);
225 mOutputStream = new BluetoothOutputStream(this);
Casper Bonde238e0f92015-04-09 09:24:48 +0200226 mMaxRxPacketSize = s.mMaxRxPacketSize;
227 mMaxTxPacketSize = s.mMaxTxPacketSize;
228
zzy3b147b72012-04-03 19:48:32 -0700229 mServiceName = s.mServiceName;
Casper Bonde238e0f92015-04-09 09:24:48 +0200230 mExcludeSdp = s.mExcludeSdp;
Casper Bonde23284232015-04-21 13:12:05 +0200231 mAuthMitm = s.mAuthMitm;
Casper Bonde3b3d1fe2015-05-08 14:32:24 +0200232 mMin16DigitPin = s.mMin16DigitPin;
zzy3b147b72012-04-03 19:48:32 -0700233 }
234 private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException {
235 BluetoothSocket as = new BluetoothSocket(this);
236 as.mSocketState = SocketState.CONNECTED;
237 FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors();
Andreas Gampee6748ce2015-12-11 18:00:38 -0800238 if (DBG) Log.d(TAG, "socket fd passed by stack fds: " + Arrays.toString(fds));
zzy3b147b72012-04-03 19:48:32 -0700239 if(fds == null || fds.length != 1) {
Andreas Gampee6748ce2015-12-11 18:00:38 -0800240 Log.e(TAG, "socket fd passed from stack failed, fds: " + Arrays.toString(fds));
zzy71bfafc2013-04-16 17:17:37 -0700241 as.close();
zzy3b147b72012-04-03 19:48:32 -0700242 throw new IOException("bt socket acept failed");
243 }
Zach Johnson569ff222015-07-13 18:00:35 -0700244
245 as.mPfd = new ParcelFileDescriptor(fds[0]);
Neil Fuller7fd72462017-01-06 11:29:15 +0000246 as.mSocket = LocalSocket.createConnectedLocalSocket(fds[0]);
zzy3b147b72012-04-03 19:48:32 -0700247 as.mSocketIS = as.mSocket.getInputStream();
248 as.mSocketOS = as.mSocket.getOutputStream();
249 as.mAddress = RemoteAddr;
250 as.mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(RemoteAddr);
251 return as;
252 }
Nick Pellybd022f42009-08-14 18:33:38 -0700253 /**
Nick Pelly16fb88a2009-10-07 07:44:03 +0200254 * Construct a BluetoothSocket from address. Used by native code.
Nick Pellybd022f42009-08-14 18:33:38 -0700255 * @param type type of socket
256 * @param fd fd to use for connected socket, or -1 for a new socket
257 * @param auth require the remote device to be authenticated
258 * @param encrypt require the connection to be encrypted
259 * @param address remote device that this socket can connect to
260 * @param port remote port
261 * @throws IOException On error, for example Bluetooth not available, or
Jake Hambyf51eada2010-09-21 13:39:53 -0700262 * insufficient privileges
Nick Pellybd022f42009-08-14 18:33:38 -0700263 */
264 private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address,
265 int port) throws IOException {
Casper Bonde3b3d1fe2015-05-08 14:32:24 +0200266 this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null, false, false);
Nick Pellybd022f42009-08-14 18:33:38 -0700267 }
268
Nick Pelly45e27042009-08-19 11:00:00 -0700269 /** @hide */
Nick Pelly0b6955a2009-05-26 19:13:43 -0700270 @Override
271 protected void finalize() throws Throwable {
272 try {
273 close();
274 } finally {
275 super.finalize();
276 }
277 }
zzy3b147b72012-04-03 19:48:32 -0700278 private int getSecurityFlags() {
279 int flags = 0;
280 if(mAuth)
281 flags |= SEC_FLAG_AUTH;
282 if(mEncrypt)
283 flags |= SEC_FLAG_ENCRYPT;
Casper Bonde238e0f92015-04-09 09:24:48 +0200284 if(mExcludeSdp)
285 flags |= BTSOCK_FLAG_NO_SDP;
Casper Bonde23284232015-04-21 13:12:05 +0200286 if(mAuthMitm)
287 flags |= SEC_FLAG_AUTH_MITM;
Casper Bonde3b3d1fe2015-05-08 14:32:24 +0200288 if(mMin16DigitPin)
289 flags |= SEC_FLAG_AUTH_16_DIGIT;
zzy3b147b72012-04-03 19:48:32 -0700290 return flags;
Nick Pelly0b6955a2009-05-26 19:13:43 -0700291 }
292
293 /**
Nick Pelly45e27042009-08-19 11:00:00 -0700294 * Get the remote device this socket is connecting, or connected, to.
295 * @return remote device
Nick Pelly0b6955a2009-05-26 19:13:43 -0700296 */
Nick Pellybd022f42009-08-14 18:33:38 -0700297 public BluetoothDevice getRemoteDevice() {
298 return mDevice;
Nick Pelly0b6955a2009-05-26 19:13:43 -0700299 }
300
301 /**
302 * Get the input stream associated with this socket.
Nick Pelly45e27042009-08-19 11:00:00 -0700303 * <p>The input stream will be returned even if the socket is not yet
Nick Pelly0b6955a2009-05-26 19:13:43 -0700304 * connected, but operations on that stream will throw IOException until
305 * the associated socket is connected.
306 * @return InputStream
307 */
308 public InputStream getInputStream() throws IOException {
309 return mInputStream;
310 }
311
312 /**
313 * Get the output stream associated with this socket.
Nick Pelly45e27042009-08-19 11:00:00 -0700314 * <p>The output stream will be returned even if the socket is not yet
Nick Pelly0b6955a2009-05-26 19:13:43 -0700315 * connected, but operations on that stream will throw IOException until
316 * the associated socket is connected.
317 * @return OutputStream
318 */
319 public OutputStream getOutputStream() throws IOException {
320 return mOutputStream;
321 }
322
Nick Pelly24bb9b82009-10-02 20:34:18 -0700323 /**
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700324 * Get the connection status of this socket, ie, whether there is an active connection with
325 * remote device.
326 * @return true if connected
327 * false if not connected
328 */
329 public boolean isConnected() {
zzy3b147b72012-04-03 19:48:32 -0700330 return mSocketState == SocketState.CONNECTED;
331 }
332
333 /*package*/ void setServiceName(String name) {
334 mServiceName = name;
335 }
336
337 /**
338 * Attempt to connect to a remote device.
339 * <p>This method will block until a connection is made or the connection
340 * fails. If this method returns without an exception then this socket
341 * is now connected.
342 * <p>Creating new connections to
343 * remote Bluetooth devices should not be attempted while device discovery
344 * is in progress. Device discovery is a heavyweight procedure on the
345 * Bluetooth adapter and will significantly slow a device connection.
346 * Use {@link BluetoothAdapter#cancelDiscovery()} to cancel an ongoing
347 * discovery. Discovery is not managed by the Activity,
348 * but is run as a system service, so an application should always call
349 * {@link BluetoothAdapter#cancelDiscovery()} even if it
350 * did not directly request a discovery, just to be sure.
351 * <p>{@link #close} can be used to abort this call from another thread.
352 * @throws IOException on error, for example connection failure
353 */
354 public void connect() throws IOException {
355 if (mDevice == null) throw new IOException("Connect is called on null device");
356
357 try {
zzy3b147b72012-04-03 19:48:32 -0700358 if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
Casper Bonde238e0f92015-04-09 09:24:48 +0200359 IBluetooth bluetoothProxy =
360 BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
fredc0f420372012-04-12 00:02:00 -0700361 if (bluetoothProxy == null) throw new IOException("Bluetooth is off");
362 mPfd = bluetoothProxy.connectSocket(mDevice, mType,
zzy3b147b72012-04-03 19:48:32 -0700363 mUuid, mPort, getSecurityFlags());
364 synchronized(this)
365 {
Matthew Xie563e4142012-10-09 22:10:37 -0700366 if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
zzy3b147b72012-04-03 19:48:32 -0700367 if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
368 if (mPfd == null) throw new IOException("bt socket connect failed");
369 FileDescriptor fd = mPfd.getFileDescriptor();
Neil Fuller7fd72462017-01-06 11:29:15 +0000370 mSocket = LocalSocket.createConnectedLocalSocket(fd);
zzy3b147b72012-04-03 19:48:32 -0700371 mSocketIS = mSocket.getInputStream();
372 mSocketOS = mSocket.getOutputStream();
373 }
374 int channel = readInt(mSocketIS);
375 if (channel <= 0)
376 throw new IOException("bt socket connect failed");
377 mPort = channel;
378 waitSocketSignal(mSocketIS);
379 synchronized(this)
380 {
381 if (mSocketState == SocketState.CLOSED)
382 throw new IOException("bt socket closed");
383 mSocketState = SocketState.CONNECTED;
384 }
385 } catch (RemoteException e) {
386 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Sharvil Nanavati3e8eb402014-04-08 14:51:15 -0700387 throw new IOException("unable to send RPC: " + e.getMessage());
zzy3b147b72012-04-03 19:48:32 -0700388 }
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700389 }
390
391 /**
Nick Pelly24bb9b82009-10-02 20:34:18 -0700392 * Currently returns unix errno instead of throwing IOException,
393 * so that BluetoothAdapter can check the error code for EADDRINUSE
394 */
395 /*package*/ int bindListen() {
zzy3b147b72012-04-03 19:48:32 -0700396 int ret;
397 if (mSocketState == SocketState.CLOSED) return EBADFD;
fredc903ac6f2012-04-24 03:59:57 -0700398 IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
fredc0f420372012-04-12 00:02:00 -0700399 if (bluetoothProxy == null) {
400 Log.e(TAG, "bindListen fail, reason: bluetooth is off");
401 return -1;
402 }
Nick Pelly71c3c782009-09-02 11:51:35 -0700403 try {
fredc0f420372012-04-12 00:02:00 -0700404 mPfd = bluetoothProxy.createSocketChannel(mType, mServiceName,
zzy3b147b72012-04-03 19:48:32 -0700405 mUuid, mPort, getSecurityFlags());
406 } catch (RemoteException e) {
407 Log.e(TAG, Log.getStackTraceString(new Throwable()));
zzy3b147b72012-04-03 19:48:32 -0700408 return -1;
409 }
410
411 // read out port number
412 try {
413 synchronized(this) {
Joe LaPennaf3de98a2014-05-13 18:17:46 -0700414 if (DBG) Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " +
Matthew Xie563e4142012-10-09 22:10:37 -0700415 mPfd);
zzy3b147b72012-04-03 19:48:32 -0700416 if(mSocketState != SocketState.INIT) return EBADFD;
417 if(mPfd == null) return -1;
418 FileDescriptor fd = mPfd.getFileDescriptor();
Neil Fuller7fd72462017-01-06 11:29:15 +0000419 if (DBG) Log.d(TAG, "bindListen(), Create LocalSocket");
420 mSocket = LocalSocket.createConnectedLocalSocket(fd);
421 if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream()");
zzy3b147b72012-04-03 19:48:32 -0700422 mSocketIS = mSocket.getInputStream();
423 mSocketOS = mSocket.getOutputStream();
424 }
Joe LaPennaf3de98a2014-05-13 18:17:46 -0700425 if (DBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS);
zzy3b147b72012-04-03 19:48:32 -0700426 int channel = readInt(mSocketIS);
427 synchronized(this) {
428 if(mSocketState == SocketState.INIT)
429 mSocketState = SocketState.LISTENING;
430 }
Joe LaPennaf3de98a2014-05-13 18:17:46 -0700431 if (DBG) Log.d(TAG, "channel: " + channel);
Casper Bonde238e0f92015-04-09 09:24:48 +0200432 if (mPort <= -1) {
zzy3b147b72012-04-03 19:48:32 -0700433 mPort = channel;
434 } // else ASSERT(mPort == channel)
435 ret = 0;
436 } catch (IOException e) {
Matthew Xie726652e2014-09-03 10:04:00 -0700437 if (mPfd != null) {
438 try {
439 mPfd.close();
440 } catch (IOException e1) {
441 Log.e(TAG, "bindListen, close mPfd: " + e1);
442 }
443 mPfd = null;
444 }
zzy3b147b72012-04-03 19:48:32 -0700445 Log.e(TAG, "bindListen, fail to get port number, exception: " + e);
446 return -1;
447 }
448 return ret;
Nick Pelly71c3c782009-09-02 11:51:35 -0700449 }
450
451 /*package*/ BluetoothSocket accept(int timeout) throws IOException {
zzy3b147b72012-04-03 19:48:32 -0700452 BluetoothSocket acceptedSocket;
Casper Bonde238e0f92015-04-09 09:24:48 +0200453 if (mSocketState != SocketState.LISTENING)
454 throw new IOException("bt socket is not in listen state");
zzy652678a2012-11-22 11:52:44 -0800455 if(timeout > 0) {
456 Log.d(TAG, "accept() set timeout (ms):" + timeout);
457 mSocket.setSoTimeout(timeout);
458 }
zzy3b147b72012-04-03 19:48:32 -0700459 String RemoteAddr = waitSocketSignal(mSocketIS);
zzy652678a2012-11-22 11:52:44 -0800460 if(timeout > 0)
461 mSocket.setSoTimeout(0);
zzy3b147b72012-04-03 19:48:32 -0700462 synchronized(this)
463 {
464 if (mSocketState != SocketState.LISTENING)
465 throw new IOException("bt socket is not in listen state");
466 acceptedSocket = acceptSocket(RemoteAddr);
467 //quick drop the reference of the file handle
Nick Pelly71c3c782009-09-02 11:51:35 -0700468 }
zzy3b147b72012-04-03 19:48:32 -0700469 return acceptedSocket;
Nick Pelly71c3c782009-09-02 11:51:35 -0700470 }
471
472 /*package*/ int available() throws IOException {
Matthew Xie563e4142012-10-09 22:10:37 -0700473 if (VDBG) Log.d(TAG, "available: " + mSocketIS);
zzy3b147b72012-04-03 19:48:32 -0700474 return mSocketIS.available();
Nick Pelly71c3c782009-09-02 11:51:35 -0700475 }
zzy71bfafc2013-04-16 17:17:37 -0700476 /**
477 * Wait until the data in sending queue is emptied. A polling version
478 * for flush implementation. Used to ensure the writing data afterwards will
479 * be packed in new RFCOMM frame.
480 * @throws IOException
481 * if an i/o error occurs.
482 */
483 /*package*/ void flush() throws IOException {
Zhihai Xu610770d2013-12-17 11:39:20 -0800484 if (mSocketOS == null) throw new IOException("flush is called on null OutputStream");
zzy71bfafc2013-04-16 17:17:37 -0700485 if (VDBG) Log.d(TAG, "flush: " + mSocketOS);
486 mSocketOS.flush();
487 }
Nick Pelly71c3c782009-09-02 11:51:35 -0700488
489 /*package*/ int read(byte[] b, int offset, int length) throws IOException {
Casper Bonde238e0f92015-04-09 09:24:48 +0200490 int ret = 0;
Zhihai Xu610770d2013-12-17 11:39:20 -0800491 if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length);
Casper Bonde238e0f92015-04-09 09:24:48 +0200492 if(mType == TYPE_L2CAP)
493 {
494 int bytesToRead = length;
495 if (VDBG) Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length
496 + "mL2capBuffer= " + mL2capBuffer);
497 if (mL2capBuffer == null) {
498 createL2capRxBuffer();
499 }
500 if (mL2capBuffer.remaining() == 0) {
501 if (VDBG) Log.v(TAG, "l2cap buffer empty, refilling...");
502 if (fillL2capRxBuffer() == -1) {
503 return -1;
504 }
505 }
506 if (bytesToRead > mL2capBuffer.remaining()) {
507 bytesToRead = mL2capBuffer.remaining();
508 }
509 if(VDBG) Log.v(TAG, "get(): offset: " + offset
510 + " bytesToRead: " + bytesToRead);
511 mL2capBuffer.get(b, offset, bytesToRead);
512 ret = bytesToRead;
513 }else {
514 if (VDBG) Log.v(TAG, "default: read(): offset: " + offset + " length:" + length);
515 ret = mSocketIS.read(b, offset, length);
516 }
517 if (ret < 0)
Zhihai Xu610770d2013-12-17 11:39:20 -0800518 throw new IOException("bt socket closed, read return: " + ret);
519 if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret);
520 return ret;
Nick Pelly71c3c782009-09-02 11:51:35 -0700521 }
522
523 /*package*/ int write(byte[] b, int offset, int length) throws IOException {
Casper Bonde238e0f92015-04-09 09:24:48 +0200524
525 //TODO: Since bindings can exist between the SDU size and the
526 // protocol, we might need to throw an exception instead of just
527 // splitting the write into multiple smaller writes.
528 // Rfcomm uses dynamic allocation, and should not have any bindings
529 // to the actual message length.
530 if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
531 if (mType == TYPE_L2CAP) {
532 if(length <= mMaxTxPacketSize) {
533 mSocketOS.write(b, offset, length);
534 } else {
Casper Bonde238e0f92015-04-09 09:24:48 +0200535 if(DBG) Log.w(TAG, "WARNING: Write buffer larger than L2CAP packet size!\n"
536 + "Packet will be divided into SDU packets of size "
537 + mMaxTxPacketSize);
Christine Hallstromfc59c342016-06-17 16:00:25 -0700538 int tmpOffset = offset;
539 int bytesToWrite = length;
540 while (bytesToWrite > 0) {
541 int tmpLength = (bytesToWrite > mMaxTxPacketSize)
542 ? mMaxTxPacketSize
543 : bytesToWrite;
Casper Bonde238e0f92015-04-09 09:24:48 +0200544 mSocketOS.write(b, tmpOffset, tmpLength);
Christine Hallstromfc59c342016-06-17 16:00:25 -0700545 tmpOffset += tmpLength;
546 bytesToWrite -= tmpLength;
547 }
Casper Bonde238e0f92015-04-09 09:24:48 +0200548 }
549 } else {
550 mSocketOS.write(b, offset, length);
551 }
552 // There is no good way to confirm since the entire process is asynchronous anyway
553 if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length);
554 return length;
Nick Pelly71c3c782009-09-02 11:51:35 -0700555 }
556
zzy3b147b72012-04-03 19:48:32 -0700557 @Override
558 public void close() throws IOException {
Casper Bonde238e0f92015-04-09 09:24:48 +0200559 if (DBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: "
560 + mSocketState);
zzy3b147b72012-04-03 19:48:32 -0700561 if(mSocketState == SocketState.CLOSED)
562 return;
563 else
564 {
565 synchronized(this)
566 {
567 if(mSocketState == SocketState.CLOSED)
568 return;
569 mSocketState = SocketState.CLOSED;
Casper Bonde238e0f92015-04-09 09:24:48 +0200570 if (DBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort +
571 ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS +
572 "mSocket: " + mSocket);
zzy3b147b72012-04-03 19:48:32 -0700573 if(mSocket != null) {
Joe LaPennaf3de98a2014-05-13 18:17:46 -0700574 if (DBG) Log.d(TAG, "Closing mSocket: " + mSocket);
zzy3b147b72012-04-03 19:48:32 -0700575 mSocket.shutdownInput();
576 mSocket.shutdownOutput();
577 mSocket.close();
578 mSocket = null;
579 }
Zhihai Xu01771022014-01-20 12:04:23 -0800580 if (mPfd != null) {
581 mPfd.close();
582 mPfd = null;
583 }
zzy3b147b72012-04-03 19:48:32 -0700584 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200585 }
zzy3b147b72012-04-03 19:48:32 -0700586 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200587
zzy3b147b72012-04-03 19:48:32 -0700588 /*package */ void removeChannel() {
589 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200590
zzy3b147b72012-04-03 19:48:32 -0700591 /*package */ int getPort() {
592 return mPort;
593 }
Casper Bonde238e0f92015-04-09 09:24:48 +0200594
595 /**
596 * Get the maximum supported Transmit packet size for the underlying transport.
597 * Use this to optimize the writes done to the output socket, to avoid sending
598 * half full packets.
599 * @return the maximum supported Transmit packet size for the underlying transport.
600 */
601 public int getMaxTransmitPacketSize(){
602 return mMaxTxPacketSize;
603 }
604
605 /**
606 * Get the maximum supported Receive packet size for the underlying transport.
607 * Use this to optimize the reads done on the input stream, as any call to read
608 * will return a maximum of this amount of bytes - or for some transports a
609 * multiple of this value.
610 * @return the maximum supported Receive packet size for the underlying transport.
611 */
612 public int getMaxReceivePacketSize(){
613 return mMaxRxPacketSize;
614 }
615
616 /**
Andre Eisenbachf4559862015-05-04 13:48:50 -0700617 * Get the type of the underlying connection.
618 * @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP}
Casper Bonde238e0f92015-04-09 09:24:48 +0200619 */
620 public int getConnectionType() {
621 return mType;
622 }
623
624 /**
625 * Change if a SDP entry should be automatically created.
626 * Must be called before calling .bind, for the call to have any effect.
627 * @param mExcludeSdp <li>TRUE - do not auto generate SDP record.
628 * <li>FALSE - default - auto generate SPP SDP record.
629 * @hide
630 */
631 public void setExcludeSdp(boolean excludeSdp) {
632 this.mExcludeSdp = excludeSdp;
633 }
634
zzy3b147b72012-04-03 19:48:32 -0700635 private String convertAddr(final byte[] addr) {
Jeff Sharkeyfea17de2013-06-11 14:13:09 -0700636 return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
zzy3b147b72012-04-03 19:48:32 -0700637 addr[0] , addr[1], addr[2], addr[3] , addr[4], addr[5]);
638 }
639 private String waitSocketSignal(InputStream is) throws IOException {
640 byte [] sig = new byte[SOCK_SIGNAL_SIZE];
641 int ret = readAll(is, sig);
Casper Bonde238e0f92015-04-09 09:24:48 +0200642 if (VDBG) Log.d(TAG, "waitSocketSignal read " + SOCK_SIGNAL_SIZE +
643 " bytes signal ret: " + ret);
zzy3b147b72012-04-03 19:48:32 -0700644 ByteBuffer bb = ByteBuffer.wrap(sig);
Casper Bonde238e0f92015-04-09 09:24:48 +0200645 /* the struct in native is decorated with __attribute__((packed)), hence this is possible */
zzy3b147b72012-04-03 19:48:32 -0700646 bb.order(ByteOrder.nativeOrder());
647 int size = bb.getShort();
zzy652678a2012-11-22 11:52:44 -0800648 if(size != SOCK_SIGNAL_SIZE)
649 throw new IOException("Connection failure, wrong signal size: " + size);
zzy3b147b72012-04-03 19:48:32 -0700650 byte [] addr = new byte[6];
651 bb.get(addr);
652 int channel = bb.getInt();
653 int status = bb.getInt();
Casper Bonde238e0f92015-04-09 09:24:48 +0200654 mMaxTxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value
655 mMaxRxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value
zzy3b147b72012-04-03 19:48:32 -0700656 String RemoteAddr = convertAddr(addr);
Matthew Xie563e4142012-10-09 22:10:37 -0700657 if (VDBG) Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: "
Casper Bonde238e0f92015-04-09 09:24:48 +0200658 + RemoteAddr + ", channel: " + channel + ", status: " + status
659 + " MaxRxPktSize: " + mMaxRxPacketSize + " MaxTxPktSize: " + mMaxTxPacketSize);
zzy3b147b72012-04-03 19:48:32 -0700660 if(status != 0)
661 throw new IOException("Connection failure, status: " + status);
662 return RemoteAddr;
663 }
Casper Bonde238e0f92015-04-09 09:24:48 +0200664
665 private void createL2capRxBuffer(){
666 if(mType == TYPE_L2CAP) {
667 // Allocate the buffer to use for reads.
668 if(VDBG) Log.v(TAG, " Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize);
669 mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]);
670 if(VDBG) Log.v(TAG, "mL2capBuffer.remaining()" + mL2capBuffer.remaining());
671 mL2capBuffer.limit(0); // Ensure we do a real read at the first read-request
672 if(VDBG) Log.v(TAG, "mL2capBuffer.remaining() after limit(0):" +
673 mL2capBuffer.remaining());
674 }
675 }
676
zzy3b147b72012-04-03 19:48:32 -0700677 private int readAll(InputStream is, byte[] b) throws IOException {
678 int left = b.length;
679 while(left > 0) {
680 int ret = is.read(b, b.length - left, left);
fredcd6883532012-04-25 17:46:13 -0700681 if(ret <= 0)
Casper Bonde238e0f92015-04-09 09:24:48 +0200682 throw new IOException("read failed, socket might closed or timeout, read ret: "
683 + ret);
zzy3b147b72012-04-03 19:48:32 -0700684 left -= ret;
685 if(left != 0)
686 Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) +
687 ", expect size: " + b.length);
Nick Pelly16fb88a2009-10-07 07:44:03 +0200688 }
zzy3b147b72012-04-03 19:48:32 -0700689 return b.length;
690 }
691
692 private int readInt(InputStream is) throws IOException {
693 byte[] ibytes = new byte[4];
694 int ret = readAll(is, ibytes);
Matthew Xie563e4142012-10-09 22:10:37 -0700695 if (VDBG) Log.d(TAG, "inputStream.read ret: " + ret);
zzy3b147b72012-04-03 19:48:32 -0700696 ByteBuffer bb = ByteBuffer.wrap(ibytes);
697 bb.order(ByteOrder.nativeOrder());
698 return bb.getInt();
Nick Pelly16fb88a2009-10-07 07:44:03 +0200699 }
Casper Bonde238e0f92015-04-09 09:24:48 +0200700
701 private int fillL2capRxBuffer() throws IOException {
702 mL2capBuffer.rewind();
703 int ret = mSocketIS.read(mL2capBuffer.array());
704 if(ret == -1) {
705 // reached end of stream - return -1
706 mL2capBuffer.limit(0);
707 return -1;
708 }
709 mL2capBuffer.limit(ret);
710 return ret;
711 }
712
713
Nick Pelly0b6955a2009-05-26 19:13:43 -0700714}