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