blob: 6bf6aa0a9efa337302295e40d30f98dcf63be0f5 [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();
Ajay Panickerc2516332017-03-28 14:28:27 -0700419 if (fd == null) {
420 Log.e(TAG, "bindListen(), null file descriptor");
421 return -1;
422 }
423
Neil Fuller7fd72462017-01-06 11:29:15 +0000424 if (DBG) Log.d(TAG, "bindListen(), Create LocalSocket");
425 mSocket = LocalSocket.createConnectedLocalSocket(fd);
426 if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream()");
zzy3b147b72012-04-03 19:48:32 -0700427 mSocketIS = mSocket.getInputStream();
428 mSocketOS = mSocket.getOutputStream();
429 }
Joe LaPennaf3de98a2014-05-13 18:17:46 -0700430 if (DBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS);
zzy3b147b72012-04-03 19:48:32 -0700431 int channel = readInt(mSocketIS);
432 synchronized(this) {
433 if(mSocketState == SocketState.INIT)
434 mSocketState = SocketState.LISTENING;
435 }
Joe LaPennaf3de98a2014-05-13 18:17:46 -0700436 if (DBG) Log.d(TAG, "channel: " + channel);
Casper Bonde238e0f92015-04-09 09:24:48 +0200437 if (mPort <= -1) {
zzy3b147b72012-04-03 19:48:32 -0700438 mPort = channel;
439 } // else ASSERT(mPort == channel)
440 ret = 0;
441 } catch (IOException e) {
Matthew Xie726652e2014-09-03 10:04:00 -0700442 if (mPfd != null) {
443 try {
444 mPfd.close();
445 } catch (IOException e1) {
446 Log.e(TAG, "bindListen, close mPfd: " + e1);
447 }
448 mPfd = null;
449 }
zzy3b147b72012-04-03 19:48:32 -0700450 Log.e(TAG, "bindListen, fail to get port number, exception: " + e);
451 return -1;
452 }
453 return ret;
Nick Pelly71c3c782009-09-02 11:51:35 -0700454 }
455
456 /*package*/ BluetoothSocket accept(int timeout) throws IOException {
zzy3b147b72012-04-03 19:48:32 -0700457 BluetoothSocket acceptedSocket;
Casper Bonde238e0f92015-04-09 09:24:48 +0200458 if (mSocketState != SocketState.LISTENING)
459 throw new IOException("bt socket is not in listen state");
zzy652678a2012-11-22 11:52:44 -0800460 if(timeout > 0) {
461 Log.d(TAG, "accept() set timeout (ms):" + timeout);
462 mSocket.setSoTimeout(timeout);
463 }
zzy3b147b72012-04-03 19:48:32 -0700464 String RemoteAddr = waitSocketSignal(mSocketIS);
zzy652678a2012-11-22 11:52:44 -0800465 if(timeout > 0)
466 mSocket.setSoTimeout(0);
zzy3b147b72012-04-03 19:48:32 -0700467 synchronized(this)
468 {
469 if (mSocketState != SocketState.LISTENING)
470 throw new IOException("bt socket is not in listen state");
471 acceptedSocket = acceptSocket(RemoteAddr);
472 //quick drop the reference of the file handle
Nick Pelly71c3c782009-09-02 11:51:35 -0700473 }
zzy3b147b72012-04-03 19:48:32 -0700474 return acceptedSocket;
Nick Pelly71c3c782009-09-02 11:51:35 -0700475 }
476
477 /*package*/ int available() throws IOException {
Matthew Xie563e4142012-10-09 22:10:37 -0700478 if (VDBG) Log.d(TAG, "available: " + mSocketIS);
zzy3b147b72012-04-03 19:48:32 -0700479 return mSocketIS.available();
Nick Pelly71c3c782009-09-02 11:51:35 -0700480 }
zzy71bfafc2013-04-16 17:17:37 -0700481 /**
482 * Wait until the data in sending queue is emptied. A polling version
483 * for flush implementation. Used to ensure the writing data afterwards will
484 * be packed in new RFCOMM frame.
485 * @throws IOException
486 * if an i/o error occurs.
487 */
488 /*package*/ void flush() throws IOException {
Zhihai Xu610770d2013-12-17 11:39:20 -0800489 if (mSocketOS == null) throw new IOException("flush is called on null OutputStream");
zzy71bfafc2013-04-16 17:17:37 -0700490 if (VDBG) Log.d(TAG, "flush: " + mSocketOS);
491 mSocketOS.flush();
492 }
Nick Pelly71c3c782009-09-02 11:51:35 -0700493
494 /*package*/ int read(byte[] b, int offset, int length) throws IOException {
Casper Bonde238e0f92015-04-09 09:24:48 +0200495 int ret = 0;
Zhihai Xu610770d2013-12-17 11:39:20 -0800496 if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length);
Casper Bonde238e0f92015-04-09 09:24:48 +0200497 if(mType == TYPE_L2CAP)
498 {
499 int bytesToRead = length;
500 if (VDBG) Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length
501 + "mL2capBuffer= " + mL2capBuffer);
502 if (mL2capBuffer == null) {
503 createL2capRxBuffer();
504 }
505 if (mL2capBuffer.remaining() == 0) {
506 if (VDBG) Log.v(TAG, "l2cap buffer empty, refilling...");
507 if (fillL2capRxBuffer() == -1) {
508 return -1;
509 }
510 }
511 if (bytesToRead > mL2capBuffer.remaining()) {
512 bytesToRead = mL2capBuffer.remaining();
513 }
514 if(VDBG) Log.v(TAG, "get(): offset: " + offset
515 + " bytesToRead: " + bytesToRead);
516 mL2capBuffer.get(b, offset, bytesToRead);
517 ret = bytesToRead;
518 }else {
519 if (VDBG) Log.v(TAG, "default: read(): offset: " + offset + " length:" + length);
520 ret = mSocketIS.read(b, offset, length);
521 }
522 if (ret < 0)
Zhihai Xu610770d2013-12-17 11:39:20 -0800523 throw new IOException("bt socket closed, read return: " + ret);
524 if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret);
525 return ret;
Nick Pelly71c3c782009-09-02 11:51:35 -0700526 }
527
528 /*package*/ int write(byte[] b, int offset, int length) throws IOException {
Casper Bonde238e0f92015-04-09 09:24:48 +0200529
530 //TODO: Since bindings can exist between the SDU size and the
531 // protocol, we might need to throw an exception instead of just
532 // splitting the write into multiple smaller writes.
533 // Rfcomm uses dynamic allocation, and should not have any bindings
534 // to the actual message length.
535 if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
536 if (mType == TYPE_L2CAP) {
537 if(length <= mMaxTxPacketSize) {
538 mSocketOS.write(b, offset, length);
539 } else {
Casper Bonde238e0f92015-04-09 09:24:48 +0200540 if(DBG) Log.w(TAG, "WARNING: Write buffer larger than L2CAP packet size!\n"
541 + "Packet will be divided into SDU packets of size "
542 + mMaxTxPacketSize);
Christine Hallstromfc59c342016-06-17 16:00:25 -0700543 int tmpOffset = offset;
544 int bytesToWrite = length;
545 while (bytesToWrite > 0) {
546 int tmpLength = (bytesToWrite > mMaxTxPacketSize)
547 ? mMaxTxPacketSize
548 : bytesToWrite;
Casper Bonde238e0f92015-04-09 09:24:48 +0200549 mSocketOS.write(b, tmpOffset, tmpLength);
Christine Hallstromfc59c342016-06-17 16:00:25 -0700550 tmpOffset += tmpLength;
551 bytesToWrite -= tmpLength;
552 }
Casper Bonde238e0f92015-04-09 09:24:48 +0200553 }
554 } else {
555 mSocketOS.write(b, offset, length);
556 }
557 // There is no good way to confirm since the entire process is asynchronous anyway
558 if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length);
559 return length;
Nick Pelly71c3c782009-09-02 11:51:35 -0700560 }
561
zzy3b147b72012-04-03 19:48:32 -0700562 @Override
563 public void close() throws IOException {
Ajay Panickerc2516332017-03-28 14:28:27 -0700564 Log.d(TAG, "close() this: " + this + ", channel: " + mPort +
565 ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS +
566 "mSocket: " + mSocket + ", mSocketState: " + mSocketState);
zzy3b147b72012-04-03 19:48:32 -0700567 if(mSocketState == SocketState.CLOSED)
568 return;
569 else
570 {
571 synchronized(this)
572 {
573 if(mSocketState == SocketState.CLOSED)
574 return;
575 mSocketState = SocketState.CLOSED;
zzy3b147b72012-04-03 19:48:32 -0700576 if(mSocket != null) {
Joe LaPennaf3de98a2014-05-13 18:17:46 -0700577 if (DBG) Log.d(TAG, "Closing mSocket: " + mSocket);
zzy3b147b72012-04-03 19:48:32 -0700578 mSocket.shutdownInput();
579 mSocket.shutdownOutput();
580 mSocket.close();
581 mSocket = null;
582 }
Zhihai Xu01771022014-01-20 12:04:23 -0800583 if (mPfd != null) {
584 mPfd.close();
585 mPfd = null;
586 }
zzy3b147b72012-04-03 19:48:32 -0700587 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200588 }
zzy3b147b72012-04-03 19:48:32 -0700589 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200590
zzy3b147b72012-04-03 19:48:32 -0700591 /*package */ void removeChannel() {
592 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200593
zzy3b147b72012-04-03 19:48:32 -0700594 /*package */ int getPort() {
595 return mPort;
596 }
Casper Bonde238e0f92015-04-09 09:24:48 +0200597
598 /**
599 * Get the maximum supported Transmit packet size for the underlying transport.
600 * Use this to optimize the writes done to the output socket, to avoid sending
601 * half full packets.
602 * @return the maximum supported Transmit packet size for the underlying transport.
603 */
604 public int getMaxTransmitPacketSize(){
605 return mMaxTxPacketSize;
606 }
607
608 /**
609 * Get the maximum supported Receive packet size for the underlying transport.
610 * Use this to optimize the reads done on the input stream, as any call to read
611 * will return a maximum of this amount of bytes - or for some transports a
612 * multiple of this value.
613 * @return the maximum supported Receive packet size for the underlying transport.
614 */
615 public int getMaxReceivePacketSize(){
616 return mMaxRxPacketSize;
617 }
618
619 /**
Andre Eisenbachf4559862015-05-04 13:48:50 -0700620 * Get the type of the underlying connection.
621 * @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP}
Casper Bonde238e0f92015-04-09 09:24:48 +0200622 */
623 public int getConnectionType() {
624 return mType;
625 }
626
627 /**
628 * Change if a SDP entry should be automatically created.
629 * Must be called before calling .bind, for the call to have any effect.
630 * @param mExcludeSdp <li>TRUE - do not auto generate SDP record.
631 * <li>FALSE - default - auto generate SPP SDP record.
632 * @hide
633 */
634 public void setExcludeSdp(boolean excludeSdp) {
635 this.mExcludeSdp = excludeSdp;
636 }
637
zzy3b147b72012-04-03 19:48:32 -0700638 private String convertAddr(final byte[] addr) {
Jeff Sharkeyfea17de2013-06-11 14:13:09 -0700639 return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
zzy3b147b72012-04-03 19:48:32 -0700640 addr[0] , addr[1], addr[2], addr[3] , addr[4], addr[5]);
641 }
642 private String waitSocketSignal(InputStream is) throws IOException {
643 byte [] sig = new byte[SOCK_SIGNAL_SIZE];
644 int ret = readAll(is, sig);
Casper Bonde238e0f92015-04-09 09:24:48 +0200645 if (VDBG) Log.d(TAG, "waitSocketSignal read " + SOCK_SIGNAL_SIZE +
646 " bytes signal ret: " + ret);
zzy3b147b72012-04-03 19:48:32 -0700647 ByteBuffer bb = ByteBuffer.wrap(sig);
Casper Bonde238e0f92015-04-09 09:24:48 +0200648 /* the struct in native is decorated with __attribute__((packed)), hence this is possible */
zzy3b147b72012-04-03 19:48:32 -0700649 bb.order(ByteOrder.nativeOrder());
650 int size = bb.getShort();
zzy652678a2012-11-22 11:52:44 -0800651 if(size != SOCK_SIGNAL_SIZE)
652 throw new IOException("Connection failure, wrong signal size: " + size);
zzy3b147b72012-04-03 19:48:32 -0700653 byte [] addr = new byte[6];
654 bb.get(addr);
655 int channel = bb.getInt();
656 int status = bb.getInt();
Casper Bonde238e0f92015-04-09 09:24:48 +0200657 mMaxTxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value
658 mMaxRxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value
zzy3b147b72012-04-03 19:48:32 -0700659 String RemoteAddr = convertAddr(addr);
Matthew Xie563e4142012-10-09 22:10:37 -0700660 if (VDBG) Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: "
Casper Bonde238e0f92015-04-09 09:24:48 +0200661 + RemoteAddr + ", channel: " + channel + ", status: " + status
662 + " MaxRxPktSize: " + mMaxRxPacketSize + " MaxTxPktSize: " + mMaxTxPacketSize);
zzy3b147b72012-04-03 19:48:32 -0700663 if(status != 0)
664 throw new IOException("Connection failure, status: " + status);
665 return RemoteAddr;
666 }
Casper Bonde238e0f92015-04-09 09:24:48 +0200667
668 private void createL2capRxBuffer(){
669 if(mType == TYPE_L2CAP) {
670 // Allocate the buffer to use for reads.
671 if(VDBG) Log.v(TAG, " Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize);
672 mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]);
673 if(VDBG) Log.v(TAG, "mL2capBuffer.remaining()" + mL2capBuffer.remaining());
674 mL2capBuffer.limit(0); // Ensure we do a real read at the first read-request
675 if(VDBG) Log.v(TAG, "mL2capBuffer.remaining() after limit(0):" +
676 mL2capBuffer.remaining());
677 }
678 }
679
zzy3b147b72012-04-03 19:48:32 -0700680 private int readAll(InputStream is, byte[] b) throws IOException {
681 int left = b.length;
682 while(left > 0) {
683 int ret = is.read(b, b.length - left, left);
fredcd6883532012-04-25 17:46:13 -0700684 if(ret <= 0)
Casper Bonde238e0f92015-04-09 09:24:48 +0200685 throw new IOException("read failed, socket might closed or timeout, read ret: "
686 + ret);
zzy3b147b72012-04-03 19:48:32 -0700687 left -= ret;
688 if(left != 0)
689 Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) +
690 ", expect size: " + b.length);
Nick Pelly16fb88a2009-10-07 07:44:03 +0200691 }
zzy3b147b72012-04-03 19:48:32 -0700692 return b.length;
693 }
694
695 private int readInt(InputStream is) throws IOException {
696 byte[] ibytes = new byte[4];
697 int ret = readAll(is, ibytes);
Matthew Xie563e4142012-10-09 22:10:37 -0700698 if (VDBG) Log.d(TAG, "inputStream.read ret: " + ret);
zzy3b147b72012-04-03 19:48:32 -0700699 ByteBuffer bb = ByteBuffer.wrap(ibytes);
700 bb.order(ByteOrder.nativeOrder());
701 return bb.getInt();
Nick Pelly16fb88a2009-10-07 07:44:03 +0200702 }
Casper Bonde238e0f92015-04-09 09:24:48 +0200703
704 private int fillL2capRxBuffer() throws IOException {
705 mL2capBuffer.rewind();
706 int ret = mSocketIS.read(mL2capBuffer.array());
707 if(ret == -1) {
708 // reached end of stream - return -1
709 mL2capBuffer.limit(0);
710 return -1;
711 }
712 mL2capBuffer.limit(ret);
713 return ret;
714 }
715
716
Nick Pelly0b6955a2009-05-26 19:13:43 -0700717}