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.ParcelUuid; 20 import android.os.ParcelFileDescriptor; 21 import android.os.RemoteException; 22 import android.util.Log; 23 24 import java.io.BufferedInputStream; 25 import java.io.Closeable; 26 import java.io.FileDescriptor; 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.io.OutputStream; 30 import java.util.Locale; 31 import java.util.UUID; 32 import android.net.LocalSocket; 33 34 import java.nio.Buffer; 35 import java.nio.ByteOrder; 36 import java.nio.ByteBuffer; 37 /** 38 * A connected or connecting Bluetooth socket. 39 * 40 * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets: 41 * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server 42 * side, use a {@link BluetoothServerSocket} to create a listening server 43 * socket. When a connection is accepted by the {@link BluetoothServerSocket}, 44 * it will return a new {@link BluetoothSocket} to manage the connection. 45 * On the client side, use a single {@link BluetoothSocket} to both initiate 46 * an outgoing connection and to manage the connection. 47 * 48 * <p>The most common type of Bluetooth socket is RFCOMM, which is the type 49 * supported by the Android APIs. RFCOMM is a connection-oriented, streaming 50 * transport over Bluetooth. It is also known as the Serial Port Profile (SPP). 51 * 52 * <p>To create a {@link BluetoothSocket} for connecting to a known device, use 53 * {@link BluetoothDevice#createRfcommSocketToServiceRecord 54 * BluetoothDevice.createRfcommSocketToServiceRecord()}. 55 * Then call {@link #connect()} to attempt a connection to the remote device. 56 * This call will block until a connection is established or the connection 57 * fails. 58 * 59 * <p>To create a {@link BluetoothSocket} as a server (or "host"), see the 60 * {@link BluetoothServerSocket} documentation. 61 * 62 * <p>Once the socket is connected, whether initiated as a client or accepted 63 * as a server, open the IO streams by calling {@link #getInputStream} and 64 * {@link #getOutputStream} in order to retrieve {@link java.io.InputStream} 65 * and {@link java.io.OutputStream} objects, respectively, which are 66 * automatically connected to the socket. 67 * 68 * <p>{@link BluetoothSocket} is thread 69 * safe. In particular, {@link #close} will always immediately abort ongoing 70 * operations and close the socket. 71 * 72 * <p class="note"><strong>Note:</strong> 73 * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. 74 * 75 * <div class="special reference"> 76 * <h3>Developer Guides</h3> 77 * <p>For more information about using Bluetooth, read the 78 * <a href="{@docRoot}guide/topics/wireless/bluetooth.html">Bluetooth</a> developer guide.</p> 79 * </div> 80 * 81 * {@see BluetoothServerSocket} 82 * {@see java.io.InputStream} 83 * {@see java.io.OutputStream} 84 */ 85 public final class BluetoothSocket implements Closeable { 86 private static final String TAG = "BluetoothSocket"; 87 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 88 private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); 89 90 /** @hide */ 91 public static final int MAX_RFCOMM_CHANNEL = 30; 92 /*package*/ static final int MAX_L2CAP_PACKAGE_SIZE = 0xFFFF; 93 94 /** RFCOMM socket */ 95 public static final int TYPE_RFCOMM = 1; 96 97 /** SCO socket */ 98 public static final int TYPE_SCO = 2; 99 100 /** L2CAP socket */ 101 public static final int TYPE_L2CAP = 3; 102 103 /*package*/ static final int EBADFD = 77; 104 /*package*/ static final int EADDRINUSE = 98; 105 106 /*package*/ static final int SEC_FLAG_ENCRYPT = 1; 107 /*package*/ static final int SEC_FLAG_AUTH = 1 << 1; 108 /*package*/ static final int BTSOCK_FLAG_NO_SDP = 1 << 2; 109 /*package*/ static final int SEC_FLAG_AUTH_MITM = 1 << 3; 110 /*package*/ static final int SEC_FLAG_AUTH_16_DIGIT = 1 << 4; 111 112 private final int mType; /* one of TYPE_RFCOMM etc */ 113 private BluetoothDevice mDevice; /* remote device */ 114 private String mAddress; /* remote address */ 115 private final boolean mAuth; 116 private final boolean mEncrypt; 117 private final BluetoothInputStream mInputStream; 118 private final BluetoothOutputStream mOutputStream; 119 private final ParcelUuid mUuid; 120 private boolean mExcludeSdp = false; /* when true no SPP SDP record will be created */ 121 private boolean mAuthMitm = false; /* when true Man-in-the-middle protection will be enabled*/ 122 private boolean mMin16DigitPin = false; /* Minimum 16 digit pin for sec mode 2 connections */ 123 private ParcelFileDescriptor mPfd; 124 private LocalSocket mSocket; 125 private InputStream mSocketIS; 126 private OutputStream mSocketOS; 127 private int mPort; /* RFCOMM channel or L2CAP psm */ 128 private int mFd; 129 private String mServiceName; 130 private static int PROXY_CONNECTION_TIMEOUT = 5000; 131 132 private static int SOCK_SIGNAL_SIZE = 20; 133 134 private ByteBuffer mL2capBuffer = null; 135 private int mMaxTxPacketSize = 0; // The l2cap maximum packet size supported by the peer. 136 private int mMaxRxPacketSize = 0; // The l2cap maximum packet size that can be received. 137 138 private enum SocketState { 139 INIT, 140 CONNECTED, 141 LISTENING, 142 CLOSED, 143 } 144 145 /** prevents all native calls after destroyNative() */ 146 private volatile SocketState mSocketState; 147 148 /** protects mSocketState */ 149 //private final ReentrantReadWriteLock mLock; 150 151 /** 152 * Construct a BluetoothSocket. 153 * @param type type of socket 154 * @param fd fd to use for connected socket, or -1 for a new socket 155 * @param auth require the remote device to be authenticated 156 * @param encrypt require the connection to be encrypted 157 * @param device remote device that this socket can connect to 158 * @param port remote port 159 * @param uuid SDP uuid 160 * @throws IOException On error, for example Bluetooth not available, or 161 * insufficient privileges 162 */ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, BluetoothDevice device, int port, ParcelUuid uuid)163 /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, 164 BluetoothDevice device, int port, ParcelUuid uuid) throws IOException { 165 this(type, fd, auth, encrypt, device, port, uuid, false, false); 166 } 167 168 /** 169 * Construct a BluetoothSocket. 170 * @param type type of socket 171 * @param fd fd to use for connected socket, or -1 for a new socket 172 * @param auth require the remote device to be authenticated 173 * @param encrypt require the connection to be encrypted 174 * @param device remote device that this socket can connect to 175 * @param port remote port 176 * @param uuid SDP uuid 177 * @param mitm enforce man-in-the-middle protection. 178 * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection 179 * @throws IOException On error, for example Bluetooth not available, or 180 * insufficient privileges 181 */ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm, boolean min16DigitPin)182 /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, 183 BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm, boolean min16DigitPin) 184 throws IOException { 185 if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type); 186 if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1 187 && port != BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { 188 if (port < 1 || port > MAX_RFCOMM_CHANNEL) { 189 throw new IOException("Invalid RFCOMM channel: " + port); 190 } 191 } 192 if (uuid != null) 193 mUuid = uuid; 194 else mUuid = new ParcelUuid(new UUID(0, 0)); 195 mType = type; 196 mAuth = auth; 197 mAuthMitm = mitm; 198 mMin16DigitPin = min16DigitPin; 199 mEncrypt = encrypt; 200 mDevice = device; 201 mPort = port; 202 mFd = fd; 203 204 mSocketState = SocketState.INIT; 205 206 if (device == null) { 207 // Server socket 208 mAddress = BluetoothAdapter.getDefaultAdapter().getAddress(); 209 } else { 210 // Remote socket 211 mAddress = device.getAddress(); 212 } 213 mInputStream = new BluetoothInputStream(this); 214 mOutputStream = new BluetoothOutputStream(this); 215 } BluetoothSocket(BluetoothSocket s)216 private BluetoothSocket(BluetoothSocket s) { 217 if (VDBG) Log.d(TAG, "Creating new Private BluetoothSocket of type: " + s.mType); 218 mUuid = s.mUuid; 219 mType = s.mType; 220 mAuth = s.mAuth; 221 mEncrypt = s.mEncrypt; 222 mPort = s.mPort; 223 mInputStream = new BluetoothInputStream(this); 224 mOutputStream = new BluetoothOutputStream(this); 225 mMaxRxPacketSize = s.mMaxRxPacketSize; 226 mMaxTxPacketSize = s.mMaxTxPacketSize; 227 228 mServiceName = s.mServiceName; 229 mExcludeSdp = s.mExcludeSdp; 230 mAuthMitm = s.mAuthMitm; 231 mMin16DigitPin = s.mMin16DigitPin; 232 } acceptSocket(String RemoteAddr)233 private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException { 234 BluetoothSocket as = new BluetoothSocket(this); 235 as.mSocketState = SocketState.CONNECTED; 236 FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors(); 237 if (DBG) Log.d(TAG, "socket fd passed by stack fds: " + fds); 238 if(fds == null || fds.length != 1) { 239 Log.e(TAG, "socket fd passed from stack failed, fds: " + fds); 240 as.close(); 241 throw new IOException("bt socket acept failed"); 242 } 243 244 as.mPfd = new ParcelFileDescriptor(fds[0]); 245 as.mSocket = new LocalSocket(fds[0]); 246 as.mSocketIS = as.mSocket.getInputStream(); 247 as.mSocketOS = as.mSocket.getOutputStream(); 248 as.mAddress = RemoteAddr; 249 as.mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(RemoteAddr); 250 return as; 251 } 252 /** 253 * Construct a BluetoothSocket from address. Used by native code. 254 * @param type type of socket 255 * @param fd fd to use for connected socket, or -1 for a new socket 256 * @param auth require the remote device to be authenticated 257 * @param encrypt require the connection to be encrypted 258 * @param address remote device that this socket can connect to 259 * @param port remote port 260 * @throws IOException On error, for example Bluetooth not available, or 261 * insufficient privileges 262 */ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, int port)263 private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, 264 int port) throws IOException { 265 this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null, false, false); 266 } 267 268 /** @hide */ 269 @Override finalize()270 protected void finalize() throws Throwable { 271 try { 272 close(); 273 } finally { 274 super.finalize(); 275 } 276 } getSecurityFlags()277 private int getSecurityFlags() { 278 int flags = 0; 279 if(mAuth) 280 flags |= SEC_FLAG_AUTH; 281 if(mEncrypt) 282 flags |= SEC_FLAG_ENCRYPT; 283 if(mExcludeSdp) 284 flags |= BTSOCK_FLAG_NO_SDP; 285 if(mAuthMitm) 286 flags |= SEC_FLAG_AUTH_MITM; 287 if(mMin16DigitPin) 288 flags |= SEC_FLAG_AUTH_16_DIGIT; 289 return flags; 290 } 291 292 /** 293 * Get the remote device this socket is connecting, or connected, to. 294 * @return remote device 295 */ getRemoteDevice()296 public BluetoothDevice getRemoteDevice() { 297 return mDevice; 298 } 299 300 /** 301 * Get the input stream associated with this socket. 302 * <p>The input stream will be returned even if the socket is not yet 303 * connected, but operations on that stream will throw IOException until 304 * the associated socket is connected. 305 * @return InputStream 306 */ getInputStream()307 public InputStream getInputStream() throws IOException { 308 return mInputStream; 309 } 310 311 /** 312 * Get the output stream associated with this socket. 313 * <p>The output stream will be returned even if the socket is not yet 314 * connected, but operations on that stream will throw IOException until 315 * the associated socket is connected. 316 * @return OutputStream 317 */ getOutputStream()318 public OutputStream getOutputStream() throws IOException { 319 return mOutputStream; 320 } 321 322 /** 323 * Get the connection status of this socket, ie, whether there is an active connection with 324 * remote device. 325 * @return true if connected 326 * false if not connected 327 */ isConnected()328 public boolean isConnected() { 329 return mSocketState == SocketState.CONNECTED; 330 } 331 setServiceName(String name)332 /*package*/ void setServiceName(String name) { 333 mServiceName = name; 334 } 335 336 /** 337 * Attempt to connect to a remote device. 338 * <p>This method will block until a connection is made or the connection 339 * fails. If this method returns without an exception then this socket 340 * is now connected. 341 * <p>Creating new connections to 342 * remote Bluetooth devices should not be attempted while device discovery 343 * is in progress. Device discovery is a heavyweight procedure on the 344 * Bluetooth adapter and will significantly slow a device connection. 345 * Use {@link BluetoothAdapter#cancelDiscovery()} to cancel an ongoing 346 * discovery. Discovery is not managed by the Activity, 347 * but is run as a system service, so an application should always call 348 * {@link BluetoothAdapter#cancelDiscovery()} even if it 349 * did not directly request a discovery, just to be sure. 350 * <p>{@link #close} can be used to abort this call from another thread. 351 * @throws IOException on error, for example connection failure 352 */ connect()353 public void connect() throws IOException { 354 if (mDevice == null) throw new IOException("Connect is called on null device"); 355 356 try { 357 if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); 358 IBluetooth bluetoothProxy = 359 BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); 360 if (bluetoothProxy == null) throw new IOException("Bluetooth is off"); 361 mPfd = bluetoothProxy.connectSocket(mDevice, mType, 362 mUuid, mPort, getSecurityFlags()); 363 synchronized(this) 364 { 365 if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd); 366 if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); 367 if (mPfd == null) throw new IOException("bt socket connect failed"); 368 FileDescriptor fd = mPfd.getFileDescriptor(); 369 mSocket = new LocalSocket(fd); 370 mSocketIS = mSocket.getInputStream(); 371 mSocketOS = mSocket.getOutputStream(); 372 } 373 int channel = readInt(mSocketIS); 374 if (channel <= 0) 375 throw new IOException("bt socket connect failed"); 376 mPort = channel; 377 waitSocketSignal(mSocketIS); 378 synchronized(this) 379 { 380 if (mSocketState == SocketState.CLOSED) 381 throw new IOException("bt socket closed"); 382 mSocketState = SocketState.CONNECTED; 383 } 384 } catch (RemoteException e) { 385 Log.e(TAG, Log.getStackTraceString(new Throwable())); 386 throw new IOException("unable to send RPC: " + e.getMessage()); 387 } 388 } 389 390 /** 391 * Currently returns unix errno instead of throwing IOException, 392 * so that BluetoothAdapter can check the error code for EADDRINUSE 393 */ bindListen()394 /*package*/ int bindListen() { 395 int ret; 396 if (mSocketState == SocketState.CLOSED) return EBADFD; 397 IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); 398 if (bluetoothProxy == null) { 399 Log.e(TAG, "bindListen fail, reason: bluetooth is off"); 400 return -1; 401 } 402 try { 403 mPfd = bluetoothProxy.createSocketChannel(mType, mServiceName, 404 mUuid, mPort, getSecurityFlags()); 405 } catch (RemoteException e) { 406 Log.e(TAG, Log.getStackTraceString(new Throwable())); 407 return -1; 408 } 409 410 // read out port number 411 try { 412 synchronized(this) { 413 if (DBG) Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + 414 mPfd); 415 if(mSocketState != SocketState.INIT) return EBADFD; 416 if(mPfd == null) return -1; 417 FileDescriptor fd = mPfd.getFileDescriptor(); 418 if (DBG) Log.d(TAG, "bindListen(), new LocalSocket "); 419 mSocket = new LocalSocket(fd); 420 if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() "); 421 mSocketIS = mSocket.getInputStream(); 422 mSocketOS = mSocket.getOutputStream(); 423 } 424 if (DBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS); 425 int channel = readInt(mSocketIS); 426 synchronized(this) { 427 if(mSocketState == SocketState.INIT) 428 mSocketState = SocketState.LISTENING; 429 } 430 if (DBG) Log.d(TAG, "channel: " + channel); 431 if (mPort <= -1) { 432 mPort = channel; 433 } // else ASSERT(mPort == channel) 434 ret = 0; 435 } catch (IOException e) { 436 if (mPfd != null) { 437 try { 438 mPfd.close(); 439 } catch (IOException e1) { 440 Log.e(TAG, "bindListen, close mPfd: " + e1); 441 } 442 mPfd = null; 443 } 444 Log.e(TAG, "bindListen, fail to get port number, exception: " + e); 445 return -1; 446 } 447 return ret; 448 } 449 accept(int timeout)450 /*package*/ BluetoothSocket accept(int timeout) throws IOException { 451 BluetoothSocket acceptedSocket; 452 if (mSocketState != SocketState.LISTENING) 453 throw new IOException("bt socket is not in listen state"); 454 if(timeout > 0) { 455 Log.d(TAG, "accept() set timeout (ms):" + timeout); 456 mSocket.setSoTimeout(timeout); 457 } 458 String RemoteAddr = waitSocketSignal(mSocketIS); 459 if(timeout > 0) 460 mSocket.setSoTimeout(0); 461 synchronized(this) 462 { 463 if (mSocketState != SocketState.LISTENING) 464 throw new IOException("bt socket is not in listen state"); 465 acceptedSocket = acceptSocket(RemoteAddr); 466 //quick drop the reference of the file handle 467 } 468 return acceptedSocket; 469 } 470 available()471 /*package*/ int available() throws IOException { 472 if (VDBG) Log.d(TAG, "available: " + mSocketIS); 473 return mSocketIS.available(); 474 } 475 /** 476 * Wait until the data in sending queue is emptied. A polling version 477 * for flush implementation. Used to ensure the writing data afterwards will 478 * be packed in new RFCOMM frame. 479 * @throws IOException 480 * if an i/o error occurs. 481 */ flush()482 /*package*/ void flush() throws IOException { 483 if (mSocketOS == null) throw new IOException("flush is called on null OutputStream"); 484 if (VDBG) Log.d(TAG, "flush: " + mSocketOS); 485 mSocketOS.flush(); 486 } 487 read(byte[] b, int offset, int length)488 /*package*/ int read(byte[] b, int offset, int length) throws IOException { 489 int ret = 0; 490 if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); 491 if(mType == TYPE_L2CAP) 492 { 493 int bytesToRead = length; 494 if (VDBG) Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length 495 + "mL2capBuffer= " + mL2capBuffer); 496 if (mL2capBuffer == null) { 497 createL2capRxBuffer(); 498 } 499 if (mL2capBuffer.remaining() == 0) { 500 if (VDBG) Log.v(TAG, "l2cap buffer empty, refilling..."); 501 if (fillL2capRxBuffer() == -1) { 502 return -1; 503 } 504 } 505 if (bytesToRead > mL2capBuffer.remaining()) { 506 bytesToRead = mL2capBuffer.remaining(); 507 } 508 if(VDBG) Log.v(TAG, "get(): offset: " + offset 509 + " bytesToRead: " + bytesToRead); 510 mL2capBuffer.get(b, offset, bytesToRead); 511 ret = bytesToRead; 512 }else { 513 if (VDBG) Log.v(TAG, "default: read(): offset: " + offset + " length:" + length); 514 ret = mSocketIS.read(b, offset, length); 515 } 516 if (ret < 0) 517 throw new IOException("bt socket closed, read return: " + ret); 518 if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); 519 return ret; 520 } 521 write(byte[] b, int offset, int length)522 /*package*/ int write(byte[] b, int offset, int length) throws IOException { 523 524 //TODO: Since bindings can exist between the SDU size and the 525 // protocol, we might need to throw an exception instead of just 526 // splitting the write into multiple smaller writes. 527 // Rfcomm uses dynamic allocation, and should not have any bindings 528 // to the actual message length. 529 if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length); 530 if (mType == TYPE_L2CAP) { 531 if(length <= mMaxTxPacketSize) { 532 mSocketOS.write(b, offset, length); 533 } else { 534 int tmpOffset = offset; 535 int tmpLength = mMaxTxPacketSize; 536 int endIndex = offset + length; 537 boolean done = false; 538 if(DBG) Log.w(TAG, "WARNING: Write buffer larger than L2CAP packet size!\n" 539 + "Packet will be divided into SDU packets of size " 540 + mMaxTxPacketSize); 541 do{ 542 mSocketOS.write(b, tmpOffset, tmpLength); 543 tmpOffset += mMaxTxPacketSize; 544 if((tmpOffset + mMaxTxPacketSize) > endIndex) { 545 tmpLength = endIndex - tmpOffset; 546 done = true; 547 } 548 } while(!done); 549 550 } 551 } else { 552 mSocketOS.write(b, offset, length); 553 } 554 // There is no good way to confirm since the entire process is asynchronous anyway 555 if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length); 556 return length; 557 } 558 559 @Override close()560 public void close() throws IOException { 561 if (DBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " 562 + mSocketState); 563 if(mSocketState == SocketState.CLOSED) 564 return; 565 else 566 { 567 synchronized(this) 568 { 569 if(mSocketState == SocketState.CLOSED) 570 return; 571 mSocketState = SocketState.CLOSED; 572 if (DBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + 573 ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS + 574 "mSocket: " + mSocket); 575 if(mSocket != null) { 576 if (DBG) Log.d(TAG, "Closing mSocket: " + mSocket); 577 mSocket.shutdownInput(); 578 mSocket.shutdownOutput(); 579 mSocket.close(); 580 mSocket = null; 581 } 582 if (mPfd != null) { 583 mPfd.close(); 584 mPfd = null; 585 } 586 } 587 } 588 } 589 removeChannel()590 /*package */ void removeChannel() { 591 } 592 getPort()593 /*package */ int getPort() { 594 return mPort; 595 } 596 597 /** 598 * Get the maximum supported Transmit packet size for the underlying transport. 599 * Use this to optimize the writes done to the output socket, to avoid sending 600 * half full packets. 601 * @return the maximum supported Transmit packet size for the underlying transport. 602 */ getMaxTransmitPacketSize()603 public int getMaxTransmitPacketSize(){ 604 return mMaxTxPacketSize; 605 } 606 607 /** 608 * Get the maximum supported Receive packet size for the underlying transport. 609 * Use this to optimize the reads done on the input stream, as any call to read 610 * will return a maximum of this amount of bytes - or for some transports a 611 * multiple of this value. 612 * @return the maximum supported Receive packet size for the underlying transport. 613 */ getMaxReceivePacketSize()614 public int getMaxReceivePacketSize(){ 615 return mMaxRxPacketSize; 616 } 617 618 /** 619 * Get the type of the underlying connection. 620 * @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP} 621 */ getConnectionType()622 public int getConnectionType() { 623 return mType; 624 } 625 626 /** 627 * Change if a SDP entry should be automatically created. 628 * Must be called before calling .bind, for the call to have any effect. 629 * @param mExcludeSdp <li>TRUE - do not auto generate SDP record. 630 * <li>FALSE - default - auto generate SPP SDP record. 631 * @hide 632 */ setExcludeSdp(boolean excludeSdp)633 public void setExcludeSdp(boolean excludeSdp) { 634 this.mExcludeSdp = excludeSdp; 635 } 636 convertAddr(final byte[] addr)637 private String convertAddr(final byte[] addr) { 638 return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", 639 addr[0] , addr[1], addr[2], addr[3] , addr[4], addr[5]); 640 } waitSocketSignal(InputStream is)641 private String waitSocketSignal(InputStream is) throws IOException { 642 byte [] sig = new byte[SOCK_SIGNAL_SIZE]; 643 int ret = readAll(is, sig); 644 if (VDBG) Log.d(TAG, "waitSocketSignal read " + SOCK_SIGNAL_SIZE + 645 " bytes signal ret: " + ret); 646 ByteBuffer bb = ByteBuffer.wrap(sig); 647 /* the struct in native is decorated with __attribute__((packed)), hence this is possible */ 648 bb.order(ByteOrder.nativeOrder()); 649 int size = bb.getShort(); 650 if(size != SOCK_SIGNAL_SIZE) 651 throw new IOException("Connection failure, wrong signal size: " + size); 652 byte [] addr = new byte[6]; 653 bb.get(addr); 654 int channel = bb.getInt(); 655 int status = bb.getInt(); 656 mMaxTxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value 657 mMaxRxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value 658 String RemoteAddr = convertAddr(addr); 659 if (VDBG) Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: " 660 + RemoteAddr + ", channel: " + channel + ", status: " + status 661 + " MaxRxPktSize: " + mMaxRxPacketSize + " MaxTxPktSize: " + mMaxTxPacketSize); 662 if(status != 0) 663 throw new IOException("Connection failure, status: " + status); 664 return RemoteAddr; 665 } 666 createL2capRxBuffer()667 private void createL2capRxBuffer(){ 668 if(mType == TYPE_L2CAP) { 669 // Allocate the buffer to use for reads. 670 if(VDBG) Log.v(TAG, " Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize); 671 mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]); 672 if(VDBG) Log.v(TAG, "mL2capBuffer.remaining()" + mL2capBuffer.remaining()); 673 mL2capBuffer.limit(0); // Ensure we do a real read at the first read-request 674 if(VDBG) Log.v(TAG, "mL2capBuffer.remaining() after limit(0):" + 675 mL2capBuffer.remaining()); 676 } 677 } 678 readAll(InputStream is, byte[] b)679 private int readAll(InputStream is, byte[] b) throws IOException { 680 int left = b.length; 681 while(left > 0) { 682 int ret = is.read(b, b.length - left, left); 683 if(ret <= 0) 684 throw new IOException("read failed, socket might closed or timeout, read ret: " 685 + ret); 686 left -= ret; 687 if(left != 0) 688 Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) + 689 ", expect size: " + b.length); 690 } 691 return b.length; 692 } 693 readInt(InputStream is)694 private int readInt(InputStream is) throws IOException { 695 byte[] ibytes = new byte[4]; 696 int ret = readAll(is, ibytes); 697 if (VDBG) Log.d(TAG, "inputStream.read ret: " + ret); 698 ByteBuffer bb = ByteBuffer.wrap(ibytes); 699 bb.order(ByteOrder.nativeOrder()); 700 return bb.getInt(); 701 } 702 fillL2capRxBuffer()703 private int fillL2capRxBuffer() throws IOException { 704 mL2capBuffer.rewind(); 705 int ret = mSocketIS.read(mL2capBuffer.array()); 706 if(ret == -1) { 707 // reached end of stream - return -1 708 mL2capBuffer.limit(0); 709 return -1; 710 } 711 mL2capBuffer.limit(ret); 712 return ret; 713 } 714 715 716 } 717