1 /* 2 * 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. 15 */ 16 17 package android.bluetooth; 18 19 import android.os.IBinder; 20 import android.os.ParcelUuid; 21 import android.os.ParcelFileDescriptor; 22 import android.os.RemoteException; 23 import android.os.ServiceManager; 24 import android.util.Log; 25 26 import java.io.Closeable; 27 import java.io.FileDescriptor; 28 import java.io.FileInputStream; 29 import java.io.FileOutputStream; 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.io.OutputStream; 33 import java.util.List; 34 import java.util.UUID; 35 import android.net.LocalSocket; 36 import java.nio.ByteOrder; 37 import java.nio.ByteBuffer; 38 /** 39 * A connected or connecting Bluetooth socket. 40 * 41 * <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 44 * socket. When a connection is accepted by the {@link BluetoothServerSocket}, 45 * it will return a new {@link BluetoothSocket} to manage the connection. 46 * On the client side, use a single {@link BluetoothSocket} to both initiate 47 * an outgoing connection and to manage the connection. 48 * 49 * <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). 52 * 53 * <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. 59 * 60 * <p>To create a {@link BluetoothSocket} as a server (or "host"), see the 61 * {@link BluetoothServerSocket} documentation. 62 * 63 * <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 70 * safe. In particular, {@link #close} will always immediately abort ongoing 71 * operations and close the socket. 72 * 73 * <p class="note"><strong>Note:</strong> 74 * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. 75 * 76 * <div class="special reference"> 77 * <h3>Developer Guides</h3> 78 * <p>For more information about using Bluetooth, read the 79 * <a href="{@docRoot}guide/topics/wireless/bluetooth.html">Bluetooth</a> developer guide.</p> 80 * </div> 81 * 82 * {@see BluetoothServerSocket} 83 * {@see java.io.InputStream} 84 * {@see java.io.OutputStream} 85 */ 86 public final class BluetoothSocket implements Closeable { 87 private static final String TAG = "BluetoothSocket"; 88 private static final boolean DBG = true; 89 private static final boolean VDBG = false; 90 91 /** @hide */ 92 public static final int MAX_RFCOMM_CHANNEL = 30; 93 94 /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */ 95 /*package*/ static final int TYPE_RFCOMM = 1; 96 /*package*/ static final int TYPE_SCO = 2; 97 /*package*/ static final int TYPE_L2CAP = 3; 98 99 /*package*/ static final int EBADFD = 77; 100 /*package*/ static final int EADDRINUSE = 98; 101 102 /*package*/ static final int SEC_FLAG_ENCRYPT = 1; 103 /*package*/ static final int SEC_FLAG_AUTH = 1 << 1; 104 105 private final int mType; /* one of TYPE_RFCOMM etc */ 106 private BluetoothDevice mDevice; /* remote device */ 107 private String mAddress; /* remote address */ 108 private final boolean mAuth; 109 private final boolean mEncrypt; 110 private final BluetoothInputStream mInputStream; 111 private final BluetoothOutputStream mOutputStream; 112 private final ParcelUuid mUuid; 113 private ParcelFileDescriptor mPfd; 114 private LocalSocket mSocket; 115 private InputStream mSocketIS; 116 private OutputStream mSocketOS; 117 private int mPort; /* RFCOMM channel or L2CAP psm */ 118 private int mFd; 119 private String mServiceName; 120 private static int PROXY_CONNECTION_TIMEOUT = 5000; 121 122 private static int SOCK_SIGNAL_SIZE = 16; 123 124 private enum SocketState { 125 INIT, 126 CONNECTED, 127 LISTENING, 128 CLOSED, 129 } 130 131 /** prevents all native calls after destroyNative() */ 132 private volatile SocketState mSocketState; 133 134 /** protects mSocketState */ 135 //private final ReentrantReadWriteLock mLock; 136 137 /** 138 * Construct a BluetoothSocket. 139 * @param type type of socket 140 * @param fd fd to use for connected socket, or -1 for a new socket 141 * @param auth require the remote device to be authenticated 142 * @param encrypt require the connection to be encrypted 143 * @param device remote device that this socket can connect to 144 * @param port remote port 145 * @param uuid SDP uuid 146 * @throws IOException On error, for example Bluetooth not available, or 147 * insufficient privileges 148 */ 149 /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, 150 BluetoothDevice device, int port, ParcelUuid uuid) throws IOException { 151 if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1) { 152 if (port < 1 || port > MAX_RFCOMM_CHANNEL) { 153 throw new IOException("Invalid RFCOMM channel: " + port); 154 } 155 } 156 if(uuid != null) 157 mUuid = uuid; 158 else mUuid = new ParcelUuid(new UUID(0, 0)); 159 mType = type; 160 mAuth = auth; 161 mEncrypt = encrypt; 162 mDevice = device; 163 mPort = port; 164 mFd = fd; 165 166 mSocketState = SocketState.INIT; 167 168 if (device == null) { 169 // Server socket 170 mAddress = BluetoothAdapter.getDefaultAdapter().getAddress(); 171 } else { 172 // Remote socket 173 mAddress = device.getAddress(); 174 } 175 mInputStream = new BluetoothInputStream(this); 176 mOutputStream = new BluetoothOutputStream(this); 177 } 178 private BluetoothSocket(BluetoothSocket s) { 179 mUuid = s.mUuid; 180 mType = s.mType; 181 mAuth = s.mAuth; 182 mEncrypt = s.mEncrypt; 183 mPort = s.mPort; 184 mInputStream = new BluetoothInputStream(this); 185 mOutputStream = new BluetoothOutputStream(this); 186 mServiceName = s.mServiceName; 187 } 188 private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException { 189 BluetoothSocket as = new BluetoothSocket(this); 190 as.mSocketState = SocketState.CONNECTED; 191 FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors(); 192 if (VDBG) Log.d(TAG, "socket fd passed by stack fds: " + fds); 193 if(fds == null || fds.length != 1) { 194 Log.e(TAG, "socket fd passed from stack failed, fds: " + fds); 195 as.close(); 196 throw new IOException("bt socket acept failed"); 197 } 198 as.mSocket = new LocalSocket(fds[0]); 199 as.mSocketIS = as.mSocket.getInputStream(); 200 as.mSocketOS = as.mSocket.getOutputStream(); 201 as.mAddress = RemoteAddr; 202 as.mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(RemoteAddr); 203 return as; 204 } 205 /** 206 * Construct a BluetoothSocket from address. Used by native code. 207 * @param type type of socket 208 * @param fd fd to use for connected socket, or -1 for a new socket 209 * @param auth require the remote device to be authenticated 210 * @param encrypt require the connection to be encrypted 211 * @param address remote device that this socket can connect to 212 * @param port remote port 213 * @throws IOException On error, for example Bluetooth not available, or 214 * insufficient privileges 215 */ 216 private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, 217 int port) throws IOException { 218 this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null); 219 } 220 221 /** @hide */ 222 @Override 223 protected void finalize() throws Throwable { 224 try { 225 close(); 226 } finally { 227 super.finalize(); 228 } 229 } 230 private int getSecurityFlags() { 231 int flags = 0; 232 if(mAuth) 233 flags |= SEC_FLAG_AUTH; 234 if(mEncrypt) 235 flags |= SEC_FLAG_ENCRYPT; 236 return flags; 237 } 238 239 /** 240 * Get the remote device this socket is connecting, or connected, to. 241 * @return remote device 242 */ 243 public BluetoothDevice getRemoteDevice() { 244 return mDevice; 245 } 246 247 /** 248 * Get the input stream associated with this socket. 249 * <p>The input stream will be returned even if the socket is not yet 250 * connected, but operations on that stream will throw IOException until 251 * the associated socket is connected. 252 * @return InputStream 253 */ 254 public InputStream getInputStream() throws IOException { 255 return mInputStream; 256 } 257 258 /** 259 * Get the output stream associated with this socket. 260 * <p>The output stream will be returned even if the socket is not yet 261 * connected, but operations on that stream will throw IOException until 262 * the associated socket is connected. 263 * @return OutputStream 264 */ 265 public OutputStream getOutputStream() throws IOException { 266 return mOutputStream; 267 } 268 269 /** 270 * Get the connection status of this socket, ie, whether there is an active connection with 271 * remote device. 272 * @return true if connected 273 * false if not connected 274 */ 275 public boolean isConnected() { 276 return mSocketState == SocketState.CONNECTED; 277 } 278 279 /*package*/ void setServiceName(String name) { 280 mServiceName = name; 281 } 282 283 /** 284 * Attempt to connect to a remote device. 285 * <p>This method will block until a connection is made or the connection 286 * fails. If this method returns without an exception then this socket 287 * is now connected. 288 * <p>Creating new connections to 289 * remote Bluetooth devices should not be attempted while device discovery 290 * is in progress. Device discovery is a heavyweight procedure on the 291 * Bluetooth adapter and will significantly slow a device connection. 292 * Use {@link BluetoothAdapter#cancelDiscovery()} to cancel an ongoing 293 * discovery. Discovery is not managed by the Activity, 294 * but is run as a system service, so an application should always call 295 * {@link BluetoothAdapter#cancelDiscovery()} even if it 296 * did not directly request a discovery, just to be sure. 297 * <p>{@link #close} can be used to abort this call from another thread. 298 * @throws IOException on error, for example connection failure 299 */ 300 public void connect() throws IOException { 301 if (mDevice == null) throw new IOException("Connect is called on null device"); 302 303 try { 304 if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); 305 IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); 306 if (bluetoothProxy == null) throw new IOException("Bluetooth is off"); 307 mPfd = bluetoothProxy.connectSocket(mDevice, mType, 308 mUuid, mPort, getSecurityFlags()); 309 synchronized(this) 310 { 311 if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd); 312 if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); 313 if (mPfd == null) throw new IOException("bt socket connect failed"); 314 FileDescriptor fd = mPfd.getFileDescriptor(); 315 mSocket = new LocalSocket(fd); 316 mSocketIS = mSocket.getInputStream(); 317 mSocketOS = mSocket.getOutputStream(); 318 } 319 int channel = readInt(mSocketIS); 320 if (channel <= 0) 321 throw new IOException("bt socket connect failed"); 322 mPort = channel; 323 waitSocketSignal(mSocketIS); 324 synchronized(this) 325 { 326 if (mSocketState == SocketState.CLOSED) 327 throw new IOException("bt socket closed"); 328 mSocketState = SocketState.CONNECTED; 329 } 330 } catch (RemoteException e) { 331 Log.e(TAG, Log.getStackTraceString(new Throwable())); 332 } 333 } 334 335 /** 336 * Currently returns unix errno instead of throwing IOException, 337 * so that BluetoothAdapter can check the error code for EADDRINUSE 338 */ 339 /*package*/ int bindListen() { 340 int ret; 341 if (mSocketState == SocketState.CLOSED) return EBADFD; 342 IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); 343 if (bluetoothProxy == null) { 344 Log.e(TAG, "bindListen fail, reason: bluetooth is off"); 345 return -1; 346 } 347 try { 348 mPfd = bluetoothProxy.createSocketChannel(mType, mServiceName, 349 mUuid, mPort, getSecurityFlags()); 350 } catch (RemoteException e) { 351 Log.e(TAG, Log.getStackTraceString(new Throwable())); 352 return -1; 353 } 354 355 // read out port number 356 try { 357 synchronized(this) { 358 if (VDBG) Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + 359 mPfd); 360 if(mSocketState != SocketState.INIT) return EBADFD; 361 if(mPfd == null) return -1; 362 FileDescriptor fd = mPfd.getFileDescriptor(); 363 if (VDBG) Log.d(TAG, "bindListen(), new LocalSocket "); 364 mSocket = new LocalSocket(fd); 365 if (VDBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() "); 366 mSocketIS = mSocket.getInputStream(); 367 mSocketOS = mSocket.getOutputStream(); 368 } 369 if (VDBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS); 370 int channel = readInt(mSocketIS); 371 synchronized(this) { 372 if(mSocketState == SocketState.INIT) 373 mSocketState = SocketState.LISTENING; 374 } 375 if (VDBG) Log.d(TAG, "channel: " + channel); 376 if (mPort == -1) { 377 mPort = channel; 378 } // else ASSERT(mPort == channel) 379 ret = 0; 380 } catch (IOException e) { 381 Log.e(TAG, "bindListen, fail to get port number, exception: " + e); 382 return -1; 383 } 384 return ret; 385 } 386 387 /*package*/ BluetoothSocket accept(int timeout) throws IOException { 388 BluetoothSocket acceptedSocket; 389 if (mSocketState != SocketState.LISTENING) throw new IOException("bt socket is not in listen state"); 390 if(timeout > 0) { 391 Log.d(TAG, "accept() set timeout (ms):" + timeout); 392 mSocket.setSoTimeout(timeout); 393 } 394 String RemoteAddr = waitSocketSignal(mSocketIS); 395 if(timeout > 0) 396 mSocket.setSoTimeout(0); 397 synchronized(this) 398 { 399 if (mSocketState != SocketState.LISTENING) 400 throw new IOException("bt socket is not in listen state"); 401 acceptedSocket = acceptSocket(RemoteAddr); 402 //quick drop the reference of the file handle 403 } 404 return acceptedSocket; 405 } 406 407 /*package*/ int available() throws IOException { 408 if (VDBG) Log.d(TAG, "available: " + mSocketIS); 409 return mSocketIS.available(); 410 } 411 /** 412 * Wait until the data in sending queue is emptied. A polling version 413 * for flush implementation. Used to ensure the writing data afterwards will 414 * be packed in new RFCOMM frame. 415 * @throws IOException 416 * if an i/o error occurs. 417 */ 418 /*package*/ void flush() throws IOException { 419 if (VDBG) Log.d(TAG, "flush: " + mSocketOS); 420 mSocketOS.flush(); 421 } 422 423 /*package*/ int read(byte[] b, int offset, int length) throws IOException { 424 425 if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); 426 int ret = mSocketIS.read(b, offset, length); 427 if(ret < 0) 428 throw new IOException("bt socket closed, read return: " + ret); 429 if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); 430 return ret; 431 } 432 433 /*package*/ int write(byte[] b, int offset, int length) throws IOException { 434 435 if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length); 436 mSocketOS.write(b, offset, length); 437 // There is no good way to confirm since the entire process is asynchronous anyway 438 if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length); 439 return length; 440 } 441 442 @Override 443 public void close() throws IOException { 444 if (VDBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " + mSocketState); 445 if(mSocketState == SocketState.CLOSED) 446 return; 447 else 448 { 449 synchronized(this) 450 { 451 if(mSocketState == SocketState.CLOSED) 452 return; 453 mSocketState = SocketState.CLOSED; 454 if (VDBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS + 455 ", mSocketOS: " + mSocketOS + "mSocket: " + mSocket); 456 if(mSocket != null) { 457 if (VDBG) Log.d(TAG, "Closing mSocket: " + mSocket); 458 mSocket.shutdownInput(); 459 mSocket.shutdownOutput(); 460 mSocket.close(); 461 mSocket = null; 462 } 463 if(mPfd != null) 464 mPfd.detachFd(); 465 } 466 } 467 } 468 469 /*package */ void removeChannel() { 470 } 471 472 /*package */ int getPort() { 473 return mPort; 474 } 475 private String convertAddr(final byte[] addr) { 476 return String.format("%02X:%02X:%02X:%02X:%02X:%02X", 477 addr[0] , addr[1], addr[2], addr[3] , addr[4], addr[5]); 478 } 479 private String waitSocketSignal(InputStream is) throws IOException { 480 byte [] sig = new byte[SOCK_SIGNAL_SIZE]; 481 int ret = readAll(is, sig); 482 if (VDBG) Log.d(TAG, "waitSocketSignal read 16 bytes signal ret: " + ret); 483 ByteBuffer bb = ByteBuffer.wrap(sig); 484 bb.order(ByteOrder.nativeOrder()); 485 int size = bb.getShort(); 486 if(size != SOCK_SIGNAL_SIZE) 487 throw new IOException("Connection failure, wrong signal size: " + size); 488 byte [] addr = new byte[6]; 489 bb.get(addr); 490 int channel = bb.getInt(); 491 int status = bb.getInt(); 492 String RemoteAddr = convertAddr(addr); 493 if (VDBG) Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: " 494 + RemoteAddr + ", channel: " + channel + ", status: " + status); 495 if(status != 0) 496 throw new IOException("Connection failure, status: " + status); 497 return RemoteAddr; 498 } 499 private int readAll(InputStream is, byte[] b) throws IOException { 500 int left = b.length; 501 while(left > 0) { 502 int ret = is.read(b, b.length - left, left); 503 if(ret <= 0) 504 throw new IOException("read failed, socket might closed or timeout, read ret: " + ret); 505 left -= ret; 506 if(left != 0) 507 Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) + 508 ", expect size: " + b.length); 509 } 510 return b.length; 511 } 512 513 private int readInt(InputStream is) throws IOException { 514 byte[] ibytes = new byte[4]; 515 int ret = readAll(is, ibytes); 516 if (VDBG) Log.d(TAG, "inputStream.read ret: " + ret); 517 ByteBuffer bb = ByteBuffer.wrap(ibytes); 518 bb.order(ByteOrder.nativeOrder()); 519 return bb.getInt(); 520 } 521 } 522