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