• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, BluetoothDevice device, int port, ParcelUuid uuid)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     }
BluetoothSocket(BluetoothSocket s)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     }
acceptSocket(String RemoteAddr)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      */
BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, int port)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
finalize()223     protected void finalize() throws Throwable {
224         try {
225             close();
226         } finally {
227             super.finalize();
228         }
229     }
getSecurityFlags()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      */
getRemoteDevice()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      */
getInputStream()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      */
getOutputStream()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      */
isConnected()275     public boolean isConnected() {
276         return mSocketState == SocketState.CONNECTED;
277     }
278 
setServiceName(String name)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      */
connect()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      */
bindListen()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 
accept(int timeout)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 
available()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      */
flush()418     /*package*/ void flush() throws IOException {
419         if (VDBG) Log.d(TAG, "flush: " + mSocketOS);
420         mSocketOS.flush();
421     }
422 
read(byte[] b, int offset, int length)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 
write(byte[] b, int offset, int length)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
close()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 
removeChannel()469     /*package */ void removeChannel() {
470     }
471 
getPort()472     /*package */ int getPort() {
473         return mPort;
474     }
convertAddr(final byte[] addr)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     }
waitSocketSignal(InputStream is)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     }
readAll(InputStream is, byte[] b)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 
readInt(InputStream is)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