BluetoothSocket.java revision 52cde7279bad58285704498eea57bdaf9e595b49
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.bluetooth; 18 19import java.io.Closeable; 20import java.io.IOException; 21import java.io.InputStream; 22import java.io.OutputStream; 23 24import java.util.concurrent.locks.ReentrantReadWriteLock; 25 26/** 27 * A connected or connecting Bluetooth socket. 28 * 29 * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets: 30 * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server 31 * side, use a {@link BluetoothServerSocket} to create a listening server 32 * socket. It will return a new, connected {@link BluetoothSocket} on an 33 * accepted connection. On the client side, use the same 34 * {@link BluetoothSocket} object to both intiate the outgoing connection, 35 * and to manage the connected socket. 36 * 37 * <p>The most common type of Bluetooth Socket is RFCOMM. RFCOMM is a 38 * connection orientated, streaming transport over Bluetooth. It is also known 39 * as the Serial Port Profile (SPP). 40 * 41 * <p>Use {@link BluetoothDevice#createRfcommSocket} to create a new {@link 42 * BluetoothSocket} ready for an outgoing connection to a remote 43 * {@link BluetoothDevice}. 44 * 45 * <p>Use {@link BluetoothAdapter#listenUsingRfcomm} to create a listening 46 * {@link BluetoothServerSocket} ready for incoming connections to the local 47 * {@link BluetoothAdapter}. 48 * 49 * <p>{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread 50 * safe. In particular, {@link #close} will always immediately abort ongoing 51 * operations and close the socket. 52 * 53 * <p>All methods on a {@link BluetoothSocket} require 54 * {@link android.Manifest.permission#BLUETOOTH} 55 */ 56public final class BluetoothSocket implements Closeable { 57 /** @hide */ 58 public static final int MAX_RFCOMM_CHANNEL = 30; 59 60 /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */ 61 /*package*/ static final int TYPE_RFCOMM = 1; 62 /*package*/ static final int TYPE_SCO = 2; 63 /*package*/ static final int TYPE_L2CAP = 3; 64 65 /*package*/ static final int EBADFD = 77; 66 /*package*/ static final int EADDRINUSE = 98; 67 68 private final int mType; /* one of TYPE_RFCOMM etc */ 69 private final int mPort; /* RFCOMM channel or L2CAP psm */ 70 private final BluetoothDevice mDevice; /* remote device */ 71 private final String mAddress; /* remote address */ 72 private final boolean mAuth; 73 private final boolean mEncrypt; 74 private final BluetoothInputStream mInputStream; 75 private final BluetoothOutputStream mOutputStream; 76 77 /** prevents all native calls after destroyNative() */ 78 private boolean mClosed; 79 80 /** protects mClosed */ 81 private final ReentrantReadWriteLock mLock; 82 83 /** used by native code only */ 84 private int mSocketData; 85 86 /** 87 * Construct a BluetoothSocket. 88 * @param type type of socket 89 * @param fd fd to use for connected socket, or -1 for a new socket 90 * @param auth require the remote device to be authenticated 91 * @param encrypt require the connection to be encrypted 92 * @param device remote device that this socket can connect to 93 * @param port remote port 94 * @throws IOException On error, for example Bluetooth not available, or 95 * insufficient priveleges 96 */ 97 /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, 98 BluetoothDevice device, int port) throws IOException { 99 if (type == BluetoothSocket.TYPE_RFCOMM) { 100 if (port < 1 || port > MAX_RFCOMM_CHANNEL) { 101 throw new IOException("Invalid RFCOMM channel: " + port); 102 } 103 } 104 mType = type; 105 mAuth = auth; 106 mEncrypt = encrypt; 107 mDevice = device; 108 if (device == null) { 109 mAddress = null; 110 } else { 111 mAddress = device.getAddress(); 112 } 113 mPort = port; 114 if (fd == -1) { 115 initSocketNative(); 116 } else { 117 initSocketFromFdNative(fd); 118 } 119 mInputStream = new BluetoothInputStream(this); 120 mOutputStream = new BluetoothOutputStream(this); 121 mClosed = false; 122 mLock = new ReentrantReadWriteLock(); 123 } 124 125 /** 126 * Construct a BluetoothSocket from address. 127 * @param type type of socket 128 * @param fd fd to use for connected socket, or -1 for a new socket 129 * @param auth require the remote device to be authenticated 130 * @param encrypt require the connection to be encrypted 131 * @param address remote device that this socket can connect to 132 * @param port remote port 133 * @throws IOException On error, for example Bluetooth not available, or 134 * insufficient priveleges 135 */ 136 private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, 137 int port) throws IOException { 138 this(type, fd, auth, encrypt, new BluetoothDevice(address), port); 139 } 140 141 /** @hide */ 142 @Override 143 protected void finalize() throws Throwable { 144 try { 145 close(); 146 } finally { 147 super.finalize(); 148 } 149 } 150 151 /** 152 * Attempt to connect to a remote device. 153 * <p>This method will block until a connection is made or the connection 154 * fails. If this method returns without an exception then this socket 155 * is now connected. 156 * <p>{@link #close} can be used to abort this call from another thread. 157 * @throws IOException on error, for example connection failure 158 */ 159 public void connect() throws IOException { 160 mLock.readLock().lock(); 161 try { 162 if (mClosed) throw new IOException("socket closed"); 163 connectNative(); 164 } finally { 165 mLock.readLock().unlock(); 166 } 167 } 168 169 /** 170 * Immediately close this socket, and release all associated resources. 171 * <p>Causes blocked calls on this socket in other threads to immediately 172 * throw an IOException. 173 */ 174 public void close() throws IOException { 175 // abort blocking operations on the socket 176 mLock.readLock().lock(); 177 try { 178 if (mClosed) return; 179 abortNative(); 180 } finally { 181 mLock.readLock().unlock(); 182 } 183 184 // all native calls are guarenteed to immediately return after 185 // abortNative(), so this lock should immediatley acquire 186 mLock.writeLock().lock(); 187 try { 188 mClosed = true; 189 destroyNative(); 190 } finally { 191 mLock.writeLock().unlock(); 192 } 193 } 194 195 /** 196 * Get the remote device this socket is connecting, or connected, to. 197 * @return remote device 198 */ 199 public BluetoothDevice getRemoteDevice() { 200 return mDevice; 201 } 202 203 /** 204 * Get the input stream associated with this socket. 205 * <p>The input stream will be returned even if the socket is not yet 206 * connected, but operations on that stream will throw IOException until 207 * the associated socket is connected. 208 * @return InputStream 209 */ 210 public InputStream getInputStream() throws IOException { 211 return mInputStream; 212 } 213 214 /** 215 * Get the output stream associated with this socket. 216 * <p>The output stream will be returned even if the socket is not yet 217 * connected, but operations on that stream will throw IOException until 218 * the associated socket is connected. 219 * @return OutputStream 220 */ 221 public OutputStream getOutputStream() throws IOException { 222 return mOutputStream; 223 } 224 225 /** 226 * Currently returns unix errno instead of throwing IOException, 227 * so that BluetoothAdapter can check the error code for EADDRINUSE 228 */ 229 /*package*/ int bindListen() { 230 mLock.readLock().lock(); 231 try { 232 if (mClosed) return EBADFD; 233 return bindListenNative(); 234 } finally { 235 mLock.readLock().unlock(); 236 } 237 } 238 239 /*package*/ BluetoothSocket accept(int timeout) throws IOException { 240 mLock.readLock().lock(); 241 try { 242 if (mClosed) throw new IOException("socket closed"); 243 return acceptNative(timeout); 244 } finally { 245 mLock.readLock().unlock(); 246 } 247 } 248 249 /*package*/ int available() throws IOException { 250 mLock.readLock().lock(); 251 try { 252 if (mClosed) throw new IOException("socket closed"); 253 return availableNative(); 254 } finally { 255 mLock.readLock().unlock(); 256 } 257 } 258 259 /*package*/ int read(byte[] b, int offset, int length) throws IOException { 260 mLock.readLock().lock(); 261 try { 262 if (mClosed) throw new IOException("socket closed"); 263 return readNative(b, offset, length); 264 } finally { 265 mLock.readLock().unlock(); 266 } 267 } 268 269 /*package*/ int write(byte[] b, int offset, int length) throws IOException { 270 mLock.readLock().lock(); 271 try { 272 if (mClosed) throw new IOException("socket closed"); 273 return writeNative(b, offset, length); 274 } finally { 275 mLock.readLock().unlock(); 276 } 277 } 278 279 private native void initSocketNative() throws IOException; 280 private native void initSocketFromFdNative(int fd) throws IOException; 281 private native void connectNative() throws IOException; 282 private native int bindListenNative(); 283 private native BluetoothSocket acceptNative(int timeout) throws IOException; 284 private native int availableNative() throws IOException; 285 private native int readNative(byte[] b, int offset, int length) throws IOException; 286 private native int writeNative(byte[] b, int offset, int length) throws IOException; 287 private native void abortNative() throws IOException; 288 private native void destroyNative() throws IOException; 289 /** 290 * Throws an IOException for given posix errno. Done natively so we can 291 * use strerr to convert to string error. 292 */ 293 /*package*/ native void throwErrnoNative(int errno) throws IOException; 294} 295