blob: 9a13c3eb070e559b4085de2b80d251047e3ae4d4 [file] [log] [blame]
Nick Pelly0b6955a2009-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
Nick Pelly16fb88a2009-10-07 07:44:03 +020019import android.bluetooth.IBluetoothCallback;
20import android.os.ParcelUuid;
21import android.os.RemoteException;
22import android.util.Log;
23
Nick Pelly0b6955a2009-05-26 19:13:43 -070024import java.io.Closeable;
25import java.io.IOException;
26import java.io.InputStream;
27import java.io.OutputStream;
Nick Pelly71c3c782009-09-02 11:51:35 -070028import java.util.concurrent.locks.ReentrantReadWriteLock;
29
Nick Pelly0b6955a2009-05-26 19:13:43 -070030/**
Nick Pelly45e27042009-08-19 11:00:00 -070031 * A connected or connecting Bluetooth socket.
Nick Pelly0b6955a2009-05-26 19:13:43 -070032 *
Nick Pelly45e27042009-08-19 11:00:00 -070033 * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:
34 * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
35 * side, use a {@link BluetoothServerSocket} to create a listening server
Scott Main9fab0ae2009-11-03 18:17:59 -080036 * socket. When a connection is accepted by the {@link BluetoothServerSocket},
37 * it will return a new {@link BluetoothSocket} to manage the connection.
Jake Hambyf51eada2010-09-21 13:39:53 -070038 * On the client side, use a single {@link BluetoothSocket} to both initiate
Scott Main9fab0ae2009-11-03 18:17:59 -080039 * an outgoing connection and to manage the connection.
Nick Pelly0b6955a2009-05-26 19:13:43 -070040 *
Scott Main9fab0ae2009-11-03 18:17:59 -080041 * <p>The most common type of Bluetooth socket is RFCOMM, which is the type
42 * supported by the Android APIs. RFCOMM is a connection-oriented, streaming
43 * transport over Bluetooth. It is also known as the Serial Port Profile (SPP).
Nick Pelly0b6955a2009-05-26 19:13:43 -070044 *
Scott Main9fab0ae2009-11-03 18:17:59 -080045 * <p>To create a {@link BluetoothSocket} for connecting to a known device, use
46 * {@link BluetoothDevice#createRfcommSocketToServiceRecord
47 * BluetoothDevice.createRfcommSocketToServiceRecord()}.
48 * Then call {@link #connect()} to attempt a connection to the remote device.
49 * This call will block until a connection is established or the connection
50 * fails.
Nick Pelly45e27042009-08-19 11:00:00 -070051 *
Scott Main9fab0ae2009-11-03 18:17:59 -080052 * <p>To create a {@link BluetoothSocket} as a server (or "host"), see the
53 * {@link BluetoothServerSocket} documentation.
Nick Pelly45e27042009-08-19 11:00:00 -070054 *
Scott Main9fab0ae2009-11-03 18:17:59 -080055 * <p>Once the socket is connected, whether initiated as a client or accepted
56 * as a server, open the IO streams by calling {@link #getInputStream} and
57 * {@link #getOutputStream} in order to retrieve {@link java.io.InputStream}
58 * and {@link java.io.OutputStream} objects, respectively, which are
59 * automatically connected to the socket.
60 *
61 * <p>{@link BluetoothSocket} is thread
Nick Pelly45e27042009-08-19 11:00:00 -070062 * safe. In particular, {@link #close} will always immediately abort ongoing
63 * operations and close the socket.
Nick Pellycf440592009-09-08 10:12:06 -070064 *
Scott Main9fab0ae2009-11-03 18:17:59 -080065 * <p class="note"><strong>Note:</strong>
66 * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
67 *
68 * {@see BluetoothServerSocket}
69 * {@see java.io.InputStream}
70 * {@see java.io.OutputStream}
Nick Pelly0b6955a2009-05-26 19:13:43 -070071 */
72public final class BluetoothSocket implements Closeable {
Nick Pelly16fb88a2009-10-07 07:44:03 +020073 private static final String TAG = "BluetoothSocket";
74
Nick Pelly24bb9b82009-10-02 20:34:18 -070075 /** @hide */
76 public static final int MAX_RFCOMM_CHANNEL = 30;
77
Nick Pelly45e27042009-08-19 11:00:00 -070078 /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */
Nick Pelly6a669fa2009-06-02 15:57:18 -070079 /*package*/ static final int TYPE_RFCOMM = 1;
80 /*package*/ static final int TYPE_SCO = 2;
81 /*package*/ static final int TYPE_L2CAP = 3;
82
Nick Pelly24bb9b82009-10-02 20:34:18 -070083 /*package*/ static final int EBADFD = 77;
84 /*package*/ static final int EADDRINUSE = 98;
85
Nick Pelly6a669fa2009-06-02 15:57:18 -070086 private final int mType; /* one of TYPE_RFCOMM etc */
Nick Pellybd022f42009-08-14 18:33:38 -070087 private final BluetoothDevice mDevice; /* remote device */
Nick Pelly0b6955a2009-05-26 19:13:43 -070088 private final String mAddress; /* remote address */
89 private final boolean mAuth;
90 private final boolean mEncrypt;
91 private final BluetoothInputStream mInputStream;
92 private final BluetoothOutputStream mOutputStream;
Nick Pelly16fb88a2009-10-07 07:44:03 +020093 private final SdpHelper mSdp;
94
95 private int mPort; /* RFCOMM channel or L2CAP psm */
Nick Pelly0b6955a2009-05-26 19:13:43 -070096
Matthew Xieceb6c9f2011-05-03 19:50:20 -070097 private enum SocketState {
98 INIT,
99 CONNECTED,
100 CLOSED
101 }
Nick Pelly71c3c782009-09-02 11:51:35 -0700102
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700103 /** prevents all native calls after destroyNative() */
104 private SocketState mSocketState;
105
106 /** protects mSocketState */
Nick Pelly71c3c782009-09-02 11:51:35 -0700107 private final ReentrantReadWriteLock mLock;
108
109 /** used by native code only */
110 private int mSocketData;
Nick Pelly0b6955a2009-05-26 19:13:43 -0700111
112 /**
Nick Pellybd022f42009-08-14 18:33:38 -0700113 * Construct a BluetoothSocket.
Nick Pelly6a669fa2009-06-02 15:57:18 -0700114 * @param type type of socket
Nick Pelly0b6955a2009-05-26 19:13:43 -0700115 * @param fd fd to use for connected socket, or -1 for a new socket
116 * @param auth require the remote device to be authenticated
117 * @param encrypt require the connection to be encrypted
Nick Pellybd022f42009-08-14 18:33:38 -0700118 * @param device remote device that this socket can connect to
Nick Pelly0b6955a2009-05-26 19:13:43 -0700119 * @param port remote port
Nick Pelly16fb88a2009-10-07 07:44:03 +0200120 * @param uuid SDP uuid
Nick Pelly0b6955a2009-05-26 19:13:43 -0700121 * @throws IOException On error, for example Bluetooth not available, or
Jake Hambyf51eada2010-09-21 13:39:53 -0700122 * insufficient privileges
Nick Pelly0b6955a2009-05-26 19:13:43 -0700123 */
Nick Pellybd022f42009-08-14 18:33:38 -0700124 /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
Nick Pelly16fb88a2009-10-07 07:44:03 +0200125 BluetoothDevice device, int port, ParcelUuid uuid) throws IOException {
126 if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1) {
Nick Pelly24bb9b82009-10-02 20:34:18 -0700127 if (port < 1 || port > MAX_RFCOMM_CHANNEL) {
128 throw new IOException("Invalid RFCOMM channel: " + port);
129 }
130 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200131 if (uuid == null) {
132 mPort = port;
133 mSdp = null;
134 } else {
135 mSdp = new SdpHelper(device, uuid);
136 mPort = -1;
137 }
Nick Pelly6a669fa2009-06-02 15:57:18 -0700138 mType = type;
Nick Pelly0b6955a2009-05-26 19:13:43 -0700139 mAuth = auth;
140 mEncrypt = encrypt;
Nick Pellybd022f42009-08-14 18:33:38 -0700141 mDevice = device;
142 if (device == null) {
143 mAddress = null;
144 } else {
145 mAddress = device.getAddress();
146 }
Nick Pelly0b6955a2009-05-26 19:13:43 -0700147 if (fd == -1) {
148 initSocketNative();
149 } else {
150 initSocketFromFdNative(fd);
151 }
152 mInputStream = new BluetoothInputStream(this);
153 mOutputStream = new BluetoothOutputStream(this);
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700154 mSocketState = SocketState.INIT;
Nick Pelly71c3c782009-09-02 11:51:35 -0700155 mLock = new ReentrantReadWriteLock();
Nick Pelly0b6955a2009-05-26 19:13:43 -0700156 }
157
Nick Pellybd022f42009-08-14 18:33:38 -0700158 /**
Nick Pelly16fb88a2009-10-07 07:44:03 +0200159 * Construct a BluetoothSocket from address. Used by native code.
Nick Pellybd022f42009-08-14 18:33:38 -0700160 * @param type type of socket
161 * @param fd fd to use for connected socket, or -1 for a new socket
162 * @param auth require the remote device to be authenticated
163 * @param encrypt require the connection to be encrypted
164 * @param address remote device that this socket can connect to
165 * @param port remote port
166 * @throws IOException On error, for example Bluetooth not available, or
Jake Hambyf51eada2010-09-21 13:39:53 -0700167 * insufficient privileges
Nick Pellybd022f42009-08-14 18:33:38 -0700168 */
169 private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address,
170 int port) throws IOException {
Nick Pelly16fb88a2009-10-07 07:44:03 +0200171 this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null);
Nick Pellybd022f42009-08-14 18:33:38 -0700172 }
173
Nick Pelly45e27042009-08-19 11:00:00 -0700174 /** @hide */
Nick Pelly0b6955a2009-05-26 19:13:43 -0700175 @Override
176 protected void finalize() throws Throwable {
177 try {
178 close();
179 } finally {
180 super.finalize();
181 }
182 }
183
184 /**
185 * Attempt to connect to a remote device.
Nick Pelly45e27042009-08-19 11:00:00 -0700186 * <p>This method will block until a connection is made or the connection
Nick Pelly0b6955a2009-05-26 19:13:43 -0700187 * fails. If this method returns without an exception then this socket
Nick Pelly45e27042009-08-19 11:00:00 -0700188 * is now connected.
Scott Main6d95fc02009-11-19 17:00:19 -0800189 * <p>Creating new connections to
190 * remote Bluetooth devices should not be attempted while device discovery
191 * is in progress. Device discovery is a heavyweight procedure on the
192 * Bluetooth adapter and will significantly slow a device connection.
193 * Use {@link BluetoothAdapter#cancelDiscovery()} to cancel an ongoing
194 * discovery. Discovery is not managed by the Activity,
195 * but is run as a system service, so an application should always call
196 * {@link BluetoothAdapter#cancelDiscovery()} even if it
197 * did not directly request a discovery, just to be sure.
Nick Pelly45e27042009-08-19 11:00:00 -0700198 * <p>{@link #close} can be used to abort this call from another thread.
199 * @throws IOException on error, for example connection failure
Nick Pelly0b6955a2009-05-26 19:13:43 -0700200 */
201 public void connect() throws IOException {
Nick Pelly71c3c782009-09-02 11:51:35 -0700202 mLock.readLock().lock();
203 try {
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700204 if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
Nick Pelly16fb88a2009-10-07 07:44:03 +0200205
206 if (mSdp != null) {
207 mPort = mSdp.doSdp(); // blocks
208 }
209
210 connectNative(); // blocks
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700211 mSocketState = SocketState.CONNECTED;
Nick Pelly71c3c782009-09-02 11:51:35 -0700212 } finally {
213 mLock.readLock().unlock();
214 }
Nick Pelly0b6955a2009-05-26 19:13:43 -0700215 }
216
217 /**
Nick Pelly45e27042009-08-19 11:00:00 -0700218 * Immediately close this socket, and release all associated resources.
219 * <p>Causes blocked calls on this socket in other threads to immediately
Nick Pelly0b6955a2009-05-26 19:13:43 -0700220 * throw an IOException.
221 */
222 public void close() throws IOException {
Nick Pelly71c3c782009-09-02 11:51:35 -0700223 // abort blocking operations on the socket
224 mLock.readLock().lock();
225 try {
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700226 if (mSocketState == SocketState.CLOSED) return;
Nick Pelly16fb88a2009-10-07 07:44:03 +0200227 if (mSdp != null) {
228 mSdp.cancel();
229 }
Nick Pelly71c3c782009-09-02 11:51:35 -0700230 abortNative();
231 } finally {
232 mLock.readLock().unlock();
233 }
234
Nick Pelly16fb88a2009-10-07 07:44:03 +0200235 // all native calls are guaranteed to immediately return after
Jake Hambyf51eada2010-09-21 13:39:53 -0700236 // abortNative(), so this lock should immediately acquire
Nick Pelly71c3c782009-09-02 11:51:35 -0700237 mLock.writeLock().lock();
238 try {
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700239 mSocketState = SocketState.CLOSED;
Nick Pelly71c3c782009-09-02 11:51:35 -0700240 destroyNative();
241 } finally {
242 mLock.writeLock().unlock();
243 }
Nick Pelly0b6955a2009-05-26 19:13:43 -0700244 }
245
246 /**
Nick Pelly45e27042009-08-19 11:00:00 -0700247 * Get the remote device this socket is connecting, or connected, to.
248 * @return remote device
Nick Pelly0b6955a2009-05-26 19:13:43 -0700249 */
Nick Pellybd022f42009-08-14 18:33:38 -0700250 public BluetoothDevice getRemoteDevice() {
251 return mDevice;
Nick Pelly0b6955a2009-05-26 19:13:43 -0700252 }
253
254 /**
255 * Get the input stream associated with this socket.
Nick Pelly45e27042009-08-19 11:00:00 -0700256 * <p>The input stream will be returned even if the socket is not yet
Nick Pelly0b6955a2009-05-26 19:13:43 -0700257 * connected, but operations on that stream will throw IOException until
258 * the associated socket is connected.
259 * @return InputStream
260 */
261 public InputStream getInputStream() throws IOException {
262 return mInputStream;
263 }
264
265 /**
266 * Get the output stream associated with this socket.
Nick Pelly45e27042009-08-19 11:00:00 -0700267 * <p>The output stream will be returned even if the socket is not yet
Nick Pelly0b6955a2009-05-26 19:13:43 -0700268 * connected, but operations on that stream will throw IOException until
269 * the associated socket is connected.
270 * @return OutputStream
271 */
272 public OutputStream getOutputStream() throws IOException {
273 return mOutputStream;
274 }
275
Nick Pelly24bb9b82009-10-02 20:34:18 -0700276 /**
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700277 * Get the connection status of this socket, ie, whether there is an active connection with
278 * remote device.
279 * @return true if connected
280 * false if not connected
281 */
282 public boolean isConnected() {
283 return (mSocketState == SocketState.CONNECTED);
284 }
285
286 /**
Nick Pelly24bb9b82009-10-02 20:34:18 -0700287 * Currently returns unix errno instead of throwing IOException,
288 * so that BluetoothAdapter can check the error code for EADDRINUSE
289 */
290 /*package*/ int bindListen() {
Nick Pelly71c3c782009-09-02 11:51:35 -0700291 mLock.readLock().lock();
292 try {
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700293 if (mSocketState == SocketState.CLOSED) return EBADFD;
Nick Pelly24bb9b82009-10-02 20:34:18 -0700294 return bindListenNative();
Nick Pelly71c3c782009-09-02 11:51:35 -0700295 } finally {
296 mLock.readLock().unlock();
297 }
298 }
299
300 /*package*/ BluetoothSocket accept(int timeout) throws IOException {
301 mLock.readLock().lock();
302 try {
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700303 if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
304
305 BluetoothSocket acceptedSocket = acceptNative(timeout);
306 mSocketState = SocketState.CONNECTED;
307 return acceptedSocket;
Nick Pelly71c3c782009-09-02 11:51:35 -0700308 } finally {
309 mLock.readLock().unlock();
310 }
311 }
312
313 /*package*/ int available() throws IOException {
314 mLock.readLock().lock();
315 try {
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700316 if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
Nick Pelly71c3c782009-09-02 11:51:35 -0700317 return availableNative();
318 } finally {
319 mLock.readLock().unlock();
320 }
321 }
322
323 /*package*/ int read(byte[] b, int offset, int length) throws IOException {
324 mLock.readLock().lock();
325 try {
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700326 if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
Nick Pelly71c3c782009-09-02 11:51:35 -0700327 return readNative(b, offset, length);
328 } finally {
329 mLock.readLock().unlock();
330 }
331 }
332
333 /*package*/ int write(byte[] b, int offset, int length) throws IOException {
334 mLock.readLock().lock();
335 try {
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700336 if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
Nick Pelly71c3c782009-09-02 11:51:35 -0700337 return writeNative(b, offset, length);
338 } finally {
339 mLock.readLock().unlock();
340 }
341 }
342
Nick Pelly6a669fa2009-06-02 15:57:18 -0700343 private native void initSocketNative() throws IOException;
344 private native void initSocketFromFdNative(int fd) throws IOException;
345 private native void connectNative() throws IOException;
Nick Pelly24bb9b82009-10-02 20:34:18 -0700346 private native int bindListenNative();
Nick Pelly71c3c782009-09-02 11:51:35 -0700347 private native BluetoothSocket acceptNative(int timeout) throws IOException;
348 private native int availableNative() throws IOException;
349 private native int readNative(byte[] b, int offset, int length) throws IOException;
350 private native int writeNative(byte[] b, int offset, int length) throws IOException;
351 private native void abortNative() throws IOException;
Nick Pelly6a669fa2009-06-02 15:57:18 -0700352 private native void destroyNative() throws IOException;
Nick Pelly24bb9b82009-10-02 20:34:18 -0700353 /**
354 * Throws an IOException for given posix errno. Done natively so we can
355 * use strerr to convert to string error.
356 */
357 /*package*/ native void throwErrnoNative(int errno) throws IOException;
Nick Pelly16fb88a2009-10-07 07:44:03 +0200358
359 /**
360 * Helper to perform blocking SDP lookup.
361 */
362 private static class SdpHelper extends IBluetoothCallback.Stub {
363 private final IBluetooth service;
364 private final ParcelUuid uuid;
365 private final BluetoothDevice device;
366 private int channel;
367 private boolean canceled;
368 public SdpHelper(BluetoothDevice device, ParcelUuid uuid) {
369 service = BluetoothDevice.getService();
370 this.device = device;
371 this.uuid = uuid;
372 canceled = false;
373 }
374 /**
375 * Returns the RFCOMM channel for the UUID, or throws IOException
376 * on failure.
377 */
378 public synchronized int doSdp() throws IOException {
379 if (canceled) throw new IOException("Service discovery canceled");
380 channel = -1;
381
382 boolean inProgress = false;
383 try {
384 inProgress = service.fetchRemoteUuids(device.getAddress(), uuid, this);
385 } catch (RemoteException e) {Log.e(TAG, "", e);}
386
387 if (!inProgress) throw new IOException("Unable to start Service Discovery");
388
389 try {
390 /* 12 second timeout as a precaution - onRfcommChannelFound
391 * should always occur before the timeout */
392 wait(12000); // block
393
394 } catch (InterruptedException e) {}
395
396 if (canceled) throw new IOException("Service discovery canceled");
397 if (channel < 1) throw new IOException("Service discovery failed");
398
399 return channel;
400 }
401 /** Object cannot be re-used after calling cancel() */
402 public synchronized void cancel() {
403 if (!canceled) {
404 canceled = true;
405 channel = -1;
406 notifyAll(); // unblock
407 }
408 }
409 public synchronized void onRfcommChannelFound(int channel) {
410 if (!canceled) {
411 this.channel = channel;
412 notifyAll(); // unblock
413 }
414 }
415 }
Nick Pelly0b6955a2009-05-26 19:13:43 -0700416}