blob: d10eaea2fba1d89459a61f3a3268e9039c65eb5e [file] [log] [blame]
Nick Pelly0b6955a2009-05-26 19:13:43 -07001/*
Zhihai Xufa0fd392012-10-23 17:31:56 -07002 * 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.
Nick Pelly0b6955a2009-05-26 19:13:43 -070015 */
16
17package android.bluetooth;
18
zzy3b147b72012-04-03 19:48:32 -070019import android.os.IBinder;
Nick Pelly16fb88a2009-10-07 07:44:03 +020020import android.os.ParcelUuid;
zzy3b147b72012-04-03 19:48:32 -070021import android.os.ParcelFileDescriptor;
Nick Pelly16fb88a2009-10-07 07:44:03 +020022import android.os.RemoteException;
zzy3b147b72012-04-03 19:48:32 -070023import android.os.ServiceManager;
Nick Pelly16fb88a2009-10-07 07:44:03 +020024import android.util.Log;
25
Nick Pelly0b6955a2009-05-26 19:13:43 -070026import java.io.Closeable;
zzy3b147b72012-04-03 19:48:32 -070027import java.io.FileDescriptor;
28import java.io.FileInputStream;
29import java.io.FileOutputStream;
Nick Pelly0b6955a2009-05-26 19:13:43 -070030import java.io.IOException;
31import java.io.InputStream;
32import java.io.OutputStream;
zzy3b147b72012-04-03 19:48:32 -070033import java.util.List;
Jeff Sharkeyfea17de2013-06-11 14:13:09 -070034import java.util.Locale;
zzyb49a8962012-10-11 14:52:43 -070035import java.util.UUID;
zzy3b147b72012-04-03 19:48:32 -070036import android.net.LocalSocket;
37import java.nio.ByteOrder;
38import java.nio.ByteBuffer;
Nick Pelly0b6955a2009-05-26 19:13:43 -070039/**
Nick Pelly45e27042009-08-19 11:00:00 -070040 * A connected or connecting Bluetooth socket.
Nick Pelly0b6955a2009-05-26 19:13:43 -070041 *
Nick Pelly45e27042009-08-19 11:00:00 -070042 * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:
43 * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
44 * side, use a {@link BluetoothServerSocket} to create a listening server
Scott Main9fab0ae2009-11-03 18:17:59 -080045 * socket. When a connection is accepted by the {@link BluetoothServerSocket},
46 * it will return a new {@link BluetoothSocket} to manage the connection.
Jake Hambyf51eada2010-09-21 13:39:53 -070047 * On the client side, use a single {@link BluetoothSocket} to both initiate
Scott Main9fab0ae2009-11-03 18:17:59 -080048 * an outgoing connection and to manage the connection.
Nick Pelly0b6955a2009-05-26 19:13:43 -070049 *
Scott Main9fab0ae2009-11-03 18:17:59 -080050 * <p>The most common type of Bluetooth socket is RFCOMM, which is the type
51 * supported by the Android APIs. RFCOMM is a connection-oriented, streaming
52 * transport over Bluetooth. It is also known as the Serial Port Profile (SPP).
Nick Pelly0b6955a2009-05-26 19:13:43 -070053 *
Scott Main9fab0ae2009-11-03 18:17:59 -080054 * <p>To create a {@link BluetoothSocket} for connecting to a known device, use
55 * {@link BluetoothDevice#createRfcommSocketToServiceRecord
56 * BluetoothDevice.createRfcommSocketToServiceRecord()}.
57 * Then call {@link #connect()} to attempt a connection to the remote device.
58 * This call will block until a connection is established or the connection
59 * fails.
Nick Pelly45e27042009-08-19 11:00:00 -070060 *
Scott Main9fab0ae2009-11-03 18:17:59 -080061 * <p>To create a {@link BluetoothSocket} as a server (or "host"), see the
62 * {@link BluetoothServerSocket} documentation.
Nick Pelly45e27042009-08-19 11:00:00 -070063 *
Scott Main9fab0ae2009-11-03 18:17:59 -080064 * <p>Once the socket is connected, whether initiated as a client or accepted
65 * as a server, open the IO streams by calling {@link #getInputStream} and
66 * {@link #getOutputStream} in order to retrieve {@link java.io.InputStream}
67 * and {@link java.io.OutputStream} objects, respectively, which are
68 * automatically connected to the socket.
69 *
70 * <p>{@link BluetoothSocket} is thread
Nick Pelly45e27042009-08-19 11:00:00 -070071 * safe. In particular, {@link #close} will always immediately abort ongoing
72 * operations and close the socket.
Nick Pellycf440592009-09-08 10:12:06 -070073 *
Scott Main9fab0ae2009-11-03 18:17:59 -080074 * <p class="note"><strong>Note:</strong>
75 * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
76 *
Joe Fernandez3aef8e1d2011-12-20 10:38:34 -080077 * <div class="special reference">
78 * <h3>Developer Guides</h3>
79 * <p>For more information about using Bluetooth, read the
80 * <a href="{@docRoot}guide/topics/wireless/bluetooth.html">Bluetooth</a> developer guide.</p>
81 * </div>
82 *
Scott Main9fab0ae2009-11-03 18:17:59 -080083 * {@see BluetoothServerSocket}
84 * {@see java.io.InputStream}
85 * {@see java.io.OutputStream}
Nick Pelly0b6955a2009-05-26 19:13:43 -070086 */
87public final class BluetoothSocket implements Closeable {
Nick Pelly16fb88a2009-10-07 07:44:03 +020088 private static final String TAG = "BluetoothSocket";
Matthew Xie563e4142012-10-09 22:10:37 -070089 private static final boolean DBG = true;
90 private static final boolean VDBG = false;
Nick Pelly16fb88a2009-10-07 07:44:03 +020091
Nick Pelly24bb9b82009-10-02 20:34:18 -070092 /** @hide */
93 public static final int MAX_RFCOMM_CHANNEL = 30;
94
Nick Pelly45e27042009-08-19 11:00:00 -070095 /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */
Nick Pelly6a669fa2009-06-02 15:57:18 -070096 /*package*/ static final int TYPE_RFCOMM = 1;
97 /*package*/ static final int TYPE_SCO = 2;
98 /*package*/ static final int TYPE_L2CAP = 3;
99
Nick Pelly24bb9b82009-10-02 20:34:18 -0700100 /*package*/ static final int EBADFD = 77;
101 /*package*/ static final int EADDRINUSE = 98;
102
zzy3b147b72012-04-03 19:48:32 -0700103 /*package*/ static final int SEC_FLAG_ENCRYPT = 1;
104 /*package*/ static final int SEC_FLAG_AUTH = 1 << 1;
105
Nick Pelly6a669fa2009-06-02 15:57:18 -0700106 private final int mType; /* one of TYPE_RFCOMM etc */
zzy3b147b72012-04-03 19:48:32 -0700107 private BluetoothDevice mDevice; /* remote device */
108 private String mAddress; /* remote address */
Nick Pelly0b6955a2009-05-26 19:13:43 -0700109 private final boolean mAuth;
110 private final boolean mEncrypt;
111 private final BluetoothInputStream mInputStream;
112 private final BluetoothOutputStream mOutputStream;
zzy3b147b72012-04-03 19:48:32 -0700113 private final ParcelUuid mUuid;
114 private ParcelFileDescriptor mPfd;
115 private LocalSocket mSocket;
116 private InputStream mSocketIS;
117 private OutputStream mSocketOS;
Nick Pelly16fb88a2009-10-07 07:44:03 +0200118 private int mPort; /* RFCOMM channel or L2CAP psm */
zzy3b147b72012-04-03 19:48:32 -0700119 private int mFd;
120 private String mServiceName;
zzy3b147b72012-04-03 19:48:32 -0700121 private static int PROXY_CONNECTION_TIMEOUT = 5000;
122
123 private static int SOCK_SIGNAL_SIZE = 16;
Nick Pelly0b6955a2009-05-26 19:13:43 -0700124
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700125 private enum SocketState {
126 INIT,
127 CONNECTED,
zzy3b147b72012-04-03 19:48:32 -0700128 LISTENING,
129 CLOSED,
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700130 }
Nick Pelly71c3c782009-09-02 11:51:35 -0700131
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700132 /** prevents all native calls after destroyNative() */
zzy3b147b72012-04-03 19:48:32 -0700133 private volatile SocketState mSocketState;
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700134
135 /** protects mSocketState */
zzy3b147b72012-04-03 19:48:32 -0700136 //private final ReentrantReadWriteLock mLock;
Nick Pelly0b6955a2009-05-26 19:13:43 -0700137
138 /**
Nick Pellybd022f42009-08-14 18:33:38 -0700139 * Construct a BluetoothSocket.
Nick Pelly6a669fa2009-06-02 15:57:18 -0700140 * @param type type of socket
Nick Pelly0b6955a2009-05-26 19:13:43 -0700141 * @param fd fd to use for connected socket, or -1 for a new socket
142 * @param auth require the remote device to be authenticated
143 * @param encrypt require the connection to be encrypted
Nick Pellybd022f42009-08-14 18:33:38 -0700144 * @param device remote device that this socket can connect to
Nick Pelly0b6955a2009-05-26 19:13:43 -0700145 * @param port remote port
Nick Pelly16fb88a2009-10-07 07:44:03 +0200146 * @param uuid SDP uuid
Nick Pelly0b6955a2009-05-26 19:13:43 -0700147 * @throws IOException On error, for example Bluetooth not available, or
Jake Hambyf51eada2010-09-21 13:39:53 -0700148 * insufficient privileges
Nick Pelly0b6955a2009-05-26 19:13:43 -0700149 */
Nick Pellybd022f42009-08-14 18:33:38 -0700150 /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
Nick Pelly16fb88a2009-10-07 07:44:03 +0200151 BluetoothDevice device, int port, ParcelUuid uuid) throws IOException {
152 if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1) {
Nick Pelly24bb9b82009-10-02 20:34:18 -0700153 if (port < 1 || port > MAX_RFCOMM_CHANNEL) {
154 throw new IOException("Invalid RFCOMM channel: " + port);
155 }
156 }
zzyb49a8962012-10-11 14:52:43 -0700157 if(uuid != null)
158 mUuid = uuid;
159 else mUuid = new ParcelUuid(new UUID(0, 0));
Nick Pelly6a669fa2009-06-02 15:57:18 -0700160 mType = type;
Nick Pelly0b6955a2009-05-26 19:13:43 -0700161 mAuth = auth;
162 mEncrypt = encrypt;
Nick Pellybd022f42009-08-14 18:33:38 -0700163 mDevice = device;
zzy3b147b72012-04-03 19:48:32 -0700164 mPort = port;
165 mFd = fd;
166
167 mSocketState = SocketState.INIT;
168
Nick Pellybd022f42009-08-14 18:33:38 -0700169 if (device == null) {
zzy3b147b72012-04-03 19:48:32 -0700170 // Server socket
171 mAddress = BluetoothAdapter.getDefaultAdapter().getAddress();
Nick Pellybd022f42009-08-14 18:33:38 -0700172 } else {
zzy3b147b72012-04-03 19:48:32 -0700173 // Remote socket
Nick Pellybd022f42009-08-14 18:33:38 -0700174 mAddress = device.getAddress();
175 }
Nick Pelly0b6955a2009-05-26 19:13:43 -0700176 mInputStream = new BluetoothInputStream(this);
177 mOutputStream = new BluetoothOutputStream(this);
zzy3b147b72012-04-03 19:48:32 -0700178 }
179 private BluetoothSocket(BluetoothSocket s) {
180 mUuid = s.mUuid;
181 mType = s.mType;
182 mAuth = s.mAuth;
183 mEncrypt = s.mEncrypt;
184 mPort = s.mPort;
185 mInputStream = new BluetoothInputStream(this);
186 mOutputStream = new BluetoothOutputStream(this);
187 mServiceName = s.mServiceName;
188 }
189 private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException {
190 BluetoothSocket as = new BluetoothSocket(this);
191 as.mSocketState = SocketState.CONNECTED;
192 FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors();
Matthew Xie563e4142012-10-09 22:10:37 -0700193 if (VDBG) Log.d(TAG, "socket fd passed by stack fds: " + fds);
zzy3b147b72012-04-03 19:48:32 -0700194 if(fds == null || fds.length != 1) {
195 Log.e(TAG, "socket fd passed from stack failed, fds: " + fds);
zzy71bfafc2013-04-16 17:17:37 -0700196 as.close();
zzy3b147b72012-04-03 19:48:32 -0700197 throw new IOException("bt socket acept failed");
198 }
199 as.mSocket = new LocalSocket(fds[0]);
200 as.mSocketIS = as.mSocket.getInputStream();
201 as.mSocketOS = as.mSocket.getOutputStream();
202 as.mAddress = RemoteAddr;
203 as.mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(RemoteAddr);
204 return as;
205 }
Nick Pellybd022f42009-08-14 18:33:38 -0700206 /**
Nick Pelly16fb88a2009-10-07 07:44:03 +0200207 * Construct a BluetoothSocket from address. Used by native code.
Nick Pellybd022f42009-08-14 18:33:38 -0700208 * @param type type of socket
209 * @param fd fd to use for connected socket, or -1 for a new socket
210 * @param auth require the remote device to be authenticated
211 * @param encrypt require the connection to be encrypted
212 * @param address remote device that this socket can connect to
213 * @param port remote port
214 * @throws IOException On error, for example Bluetooth not available, or
Jake Hambyf51eada2010-09-21 13:39:53 -0700215 * insufficient privileges
Nick Pellybd022f42009-08-14 18:33:38 -0700216 */
217 private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address,
218 int port) throws IOException {
Nick Pelly16fb88a2009-10-07 07:44:03 +0200219 this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null);
Nick Pellybd022f42009-08-14 18:33:38 -0700220 }
221
Nick Pelly45e27042009-08-19 11:00:00 -0700222 /** @hide */
Nick Pelly0b6955a2009-05-26 19:13:43 -0700223 @Override
224 protected void finalize() throws Throwable {
225 try {
226 close();
227 } finally {
228 super.finalize();
229 }
230 }
zzy3b147b72012-04-03 19:48:32 -0700231 private int getSecurityFlags() {
232 int flags = 0;
233 if(mAuth)
234 flags |= SEC_FLAG_AUTH;
235 if(mEncrypt)
236 flags |= SEC_FLAG_ENCRYPT;
237 return flags;
Nick Pelly0b6955a2009-05-26 19:13:43 -0700238 }
239
240 /**
Nick Pelly45e27042009-08-19 11:00:00 -0700241 * Get the remote device this socket is connecting, or connected, to.
242 * @return remote device
Nick Pelly0b6955a2009-05-26 19:13:43 -0700243 */
Nick Pellybd022f42009-08-14 18:33:38 -0700244 public BluetoothDevice getRemoteDevice() {
245 return mDevice;
Nick Pelly0b6955a2009-05-26 19:13:43 -0700246 }
247
248 /**
249 * Get the input stream associated with this socket.
Nick Pelly45e27042009-08-19 11:00:00 -0700250 * <p>The input stream will be returned even if the socket is not yet
Nick Pelly0b6955a2009-05-26 19:13:43 -0700251 * connected, but operations on that stream will throw IOException until
252 * the associated socket is connected.
253 * @return InputStream
254 */
255 public InputStream getInputStream() throws IOException {
256 return mInputStream;
257 }
258
259 /**
260 * Get the output stream associated with this socket.
Nick Pelly45e27042009-08-19 11:00:00 -0700261 * <p>The output stream will be returned even if the socket is not yet
Nick Pelly0b6955a2009-05-26 19:13:43 -0700262 * connected, but operations on that stream will throw IOException until
263 * the associated socket is connected.
264 * @return OutputStream
265 */
266 public OutputStream getOutputStream() throws IOException {
267 return mOutputStream;
268 }
269
Nick Pelly24bb9b82009-10-02 20:34:18 -0700270 /**
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700271 * Get the connection status of this socket, ie, whether there is an active connection with
272 * remote device.
273 * @return true if connected
274 * false if not connected
275 */
276 public boolean isConnected() {
zzy3b147b72012-04-03 19:48:32 -0700277 return mSocketState == SocketState.CONNECTED;
278 }
279
280 /*package*/ void setServiceName(String name) {
281 mServiceName = name;
282 }
283
284 /**
285 * Attempt to connect to a remote device.
286 * <p>This method will block until a connection is made or the connection
287 * fails. If this method returns without an exception then this socket
288 * is now connected.
289 * <p>Creating new connections to
290 * remote Bluetooth devices should not be attempted while device discovery
291 * is in progress. Device discovery is a heavyweight procedure on the
292 * Bluetooth adapter and will significantly slow a device connection.
293 * Use {@link BluetoothAdapter#cancelDiscovery()} to cancel an ongoing
294 * discovery. Discovery is not managed by the Activity,
295 * but is run as a system service, so an application should always call
296 * {@link BluetoothAdapter#cancelDiscovery()} even if it
297 * did not directly request a discovery, just to be sure.
298 * <p>{@link #close} can be used to abort this call from another thread.
299 * @throws IOException on error, for example connection failure
300 */
301 public void connect() throws IOException {
302 if (mDevice == null) throw new IOException("Connect is called on null device");
303
304 try {
zzy3b147b72012-04-03 19:48:32 -0700305 if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
fredc903ac6f2012-04-24 03:59:57 -0700306 IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
fredc0f420372012-04-12 00:02:00 -0700307 if (bluetoothProxy == null) throw new IOException("Bluetooth is off");
308 mPfd = bluetoothProxy.connectSocket(mDevice, mType,
zzy3b147b72012-04-03 19:48:32 -0700309 mUuid, mPort, getSecurityFlags());
310 synchronized(this)
311 {
Matthew Xie563e4142012-10-09 22:10:37 -0700312 if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
zzy3b147b72012-04-03 19:48:32 -0700313 if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
314 if (mPfd == null) throw new IOException("bt socket connect failed");
315 FileDescriptor fd = mPfd.getFileDescriptor();
316 mSocket = new LocalSocket(fd);
317 mSocketIS = mSocket.getInputStream();
318 mSocketOS = mSocket.getOutputStream();
319 }
320 int channel = readInt(mSocketIS);
321 if (channel <= 0)
322 throw new IOException("bt socket connect failed");
323 mPort = channel;
324 waitSocketSignal(mSocketIS);
325 synchronized(this)
326 {
327 if (mSocketState == SocketState.CLOSED)
328 throw new IOException("bt socket closed");
329 mSocketState = SocketState.CONNECTED;
330 }
331 } catch (RemoteException e) {
332 Log.e(TAG, Log.getStackTraceString(new Throwable()));
333 }
Matthew Xieceb6c9f2011-05-03 19:50:20 -0700334 }
335
336 /**
Nick Pelly24bb9b82009-10-02 20:34:18 -0700337 * Currently returns unix errno instead of throwing IOException,
338 * so that BluetoothAdapter can check the error code for EADDRINUSE
339 */
340 /*package*/ int bindListen() {
zzy3b147b72012-04-03 19:48:32 -0700341 int ret;
342 if (mSocketState == SocketState.CLOSED) return EBADFD;
fredc903ac6f2012-04-24 03:59:57 -0700343 IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
fredc0f420372012-04-12 00:02:00 -0700344 if (bluetoothProxy == null) {
345 Log.e(TAG, "bindListen fail, reason: bluetooth is off");
346 return -1;
347 }
Nick Pelly71c3c782009-09-02 11:51:35 -0700348 try {
fredc0f420372012-04-12 00:02:00 -0700349 mPfd = bluetoothProxy.createSocketChannel(mType, mServiceName,
zzy3b147b72012-04-03 19:48:32 -0700350 mUuid, mPort, getSecurityFlags());
351 } catch (RemoteException e) {
352 Log.e(TAG, Log.getStackTraceString(new Throwable()));
zzy3b147b72012-04-03 19:48:32 -0700353 return -1;
354 }
355
356 // read out port number
357 try {
358 synchronized(this) {
Matthew Xie563e4142012-10-09 22:10:37 -0700359 if (VDBG) Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " +
360 mPfd);
zzy3b147b72012-04-03 19:48:32 -0700361 if(mSocketState != SocketState.INIT) return EBADFD;
362 if(mPfd == null) return -1;
363 FileDescriptor fd = mPfd.getFileDescriptor();
Matthew Xie563e4142012-10-09 22:10:37 -0700364 if (VDBG) Log.d(TAG, "bindListen(), new LocalSocket ");
zzy3b147b72012-04-03 19:48:32 -0700365 mSocket = new LocalSocket(fd);
Matthew Xie563e4142012-10-09 22:10:37 -0700366 if (VDBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() ");
zzy3b147b72012-04-03 19:48:32 -0700367 mSocketIS = mSocket.getInputStream();
368 mSocketOS = mSocket.getOutputStream();
369 }
Matthew Xie563e4142012-10-09 22:10:37 -0700370 if (VDBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS);
zzy3b147b72012-04-03 19:48:32 -0700371 int channel = readInt(mSocketIS);
372 synchronized(this) {
373 if(mSocketState == SocketState.INIT)
374 mSocketState = SocketState.LISTENING;
375 }
Matthew Xie563e4142012-10-09 22:10:37 -0700376 if (VDBG) Log.d(TAG, "channel: " + channel);
zzy3b147b72012-04-03 19:48:32 -0700377 if (mPort == -1) {
378 mPort = channel;
379 } // else ASSERT(mPort == channel)
380 ret = 0;
381 } catch (IOException e) {
382 Log.e(TAG, "bindListen, fail to get port number, exception: " + e);
383 return -1;
384 }
385 return ret;
Nick Pelly71c3c782009-09-02 11:51:35 -0700386 }
387
388 /*package*/ BluetoothSocket accept(int timeout) throws IOException {
zzy3b147b72012-04-03 19:48:32 -0700389 BluetoothSocket acceptedSocket;
390 if (mSocketState != SocketState.LISTENING) throw new IOException("bt socket is not in listen state");
zzy652678a2012-11-22 11:52:44 -0800391 if(timeout > 0) {
392 Log.d(TAG, "accept() set timeout (ms):" + timeout);
393 mSocket.setSoTimeout(timeout);
394 }
zzy3b147b72012-04-03 19:48:32 -0700395 String RemoteAddr = waitSocketSignal(mSocketIS);
zzy652678a2012-11-22 11:52:44 -0800396 if(timeout > 0)
397 mSocket.setSoTimeout(0);
zzy3b147b72012-04-03 19:48:32 -0700398 synchronized(this)
399 {
400 if (mSocketState != SocketState.LISTENING)
401 throw new IOException("bt socket is not in listen state");
402 acceptedSocket = acceptSocket(RemoteAddr);
403 //quick drop the reference of the file handle
Nick Pelly71c3c782009-09-02 11:51:35 -0700404 }
zzy3b147b72012-04-03 19:48:32 -0700405 return acceptedSocket;
Nick Pelly71c3c782009-09-02 11:51:35 -0700406 }
407
408 /*package*/ int available() throws IOException {
Matthew Xie563e4142012-10-09 22:10:37 -0700409 if (VDBG) Log.d(TAG, "available: " + mSocketIS);
zzy3b147b72012-04-03 19:48:32 -0700410 return mSocketIS.available();
Nick Pelly71c3c782009-09-02 11:51:35 -0700411 }
zzy71bfafc2013-04-16 17:17:37 -0700412 /**
413 * Wait until the data in sending queue is emptied. A polling version
414 * for flush implementation. Used to ensure the writing data afterwards will
415 * be packed in new RFCOMM frame.
416 * @throws IOException
417 * if an i/o error occurs.
418 */
419 /*package*/ void flush() throws IOException {
420 if (VDBG) Log.d(TAG, "flush: " + mSocketOS);
421 mSocketOS.flush();
422 }
Nick Pelly71c3c782009-09-02 11:51:35 -0700423
424 /*package*/ int read(byte[] b, int offset, int length) throws IOException {
zzy3b147b72012-04-03 19:48:32 -0700425
Matthew Xie563e4142012-10-09 22:10:37 -0700426 if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length);
zzy3b147b72012-04-03 19:48:32 -0700427 int ret = mSocketIS.read(b, offset, length);
428 if(ret < 0)
429 throw new IOException("bt socket closed, read return: " + ret);
Matthew Xie563e4142012-10-09 22:10:37 -0700430 if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret);
zzy3b147b72012-04-03 19:48:32 -0700431 return ret;
Nick Pelly71c3c782009-09-02 11:51:35 -0700432 }
433
434 /*package*/ int write(byte[] b, int offset, int length) throws IOException {
zzy3b147b72012-04-03 19:48:32 -0700435
Matthew Xie563e4142012-10-09 22:10:37 -0700436 if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
zzy3b147b72012-04-03 19:48:32 -0700437 mSocketOS.write(b, offset, length);
438 // There is no good way to confirm since the entire process is asynchronous anyway
Matthew Xie563e4142012-10-09 22:10:37 -0700439 if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length);
zzy3b147b72012-04-03 19:48:32 -0700440 return length;
Nick Pelly71c3c782009-09-02 11:51:35 -0700441 }
442
zzy3b147b72012-04-03 19:48:32 -0700443 @Override
444 public void close() throws IOException {
Matthew Xied77982e2012-11-29 20:26:19 -0800445 if (VDBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " + mSocketState);
zzy3b147b72012-04-03 19:48:32 -0700446 if(mSocketState == SocketState.CLOSED)
447 return;
448 else
449 {
450 synchronized(this)
451 {
452 if(mSocketState == SocketState.CLOSED)
453 return;
454 mSocketState = SocketState.CLOSED;
Matthew Xie563e4142012-10-09 22:10:37 -0700455 if (VDBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS +
zzy3b147b72012-04-03 19:48:32 -0700456 ", mSocketOS: " + mSocketOS + "mSocket: " + mSocket);
457 if(mSocket != null) {
Matthew Xie563e4142012-10-09 22:10:37 -0700458 if (VDBG) Log.d(TAG, "Closing mSocket: " + mSocket);
zzy3b147b72012-04-03 19:48:32 -0700459 mSocket.shutdownInput();
460 mSocket.shutdownOutput();
461 mSocket.close();
462 mSocket = null;
463 }
464 if(mPfd != null)
465 mPfd.detachFd();
466 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200467 }
zzy3b147b72012-04-03 19:48:32 -0700468 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200469
zzy3b147b72012-04-03 19:48:32 -0700470 /*package */ void removeChannel() {
471 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200472
zzy3b147b72012-04-03 19:48:32 -0700473 /*package */ int getPort() {
474 return mPort;
475 }
476 private String convertAddr(final byte[] addr) {
Jeff Sharkeyfea17de2013-06-11 14:13:09 -0700477 return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
zzy3b147b72012-04-03 19:48:32 -0700478 addr[0] , addr[1], addr[2], addr[3] , addr[4], addr[5]);
479 }
480 private String waitSocketSignal(InputStream is) throws IOException {
481 byte [] sig = new byte[SOCK_SIGNAL_SIZE];
482 int ret = readAll(is, sig);
Matthew Xie563e4142012-10-09 22:10:37 -0700483 if (VDBG) Log.d(TAG, "waitSocketSignal read 16 bytes signal ret: " + ret);
zzy3b147b72012-04-03 19:48:32 -0700484 ByteBuffer bb = ByteBuffer.wrap(sig);
485 bb.order(ByteOrder.nativeOrder());
486 int size = bb.getShort();
zzy652678a2012-11-22 11:52:44 -0800487 if(size != SOCK_SIGNAL_SIZE)
488 throw new IOException("Connection failure, wrong signal size: " + size);
zzy3b147b72012-04-03 19:48:32 -0700489 byte [] addr = new byte[6];
490 bb.get(addr);
491 int channel = bb.getInt();
492 int status = bb.getInt();
493 String RemoteAddr = convertAddr(addr);
Matthew Xie563e4142012-10-09 22:10:37 -0700494 if (VDBG) Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: "
zzy3b147b72012-04-03 19:48:32 -0700495 + RemoteAddr + ", channel: " + channel + ", status: " + status);
496 if(status != 0)
497 throw new IOException("Connection failure, status: " + status);
498 return RemoteAddr;
499 }
500 private int readAll(InputStream is, byte[] b) throws IOException {
501 int left = b.length;
502 while(left > 0) {
503 int ret = is.read(b, b.length - left, left);
fredcd6883532012-04-25 17:46:13 -0700504 if(ret <= 0)
zzy652678a2012-11-22 11:52:44 -0800505 throw new IOException("read failed, socket might closed or timeout, read ret: " + ret);
zzy3b147b72012-04-03 19:48:32 -0700506 left -= ret;
507 if(left != 0)
508 Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) +
509 ", expect size: " + b.length);
Nick Pelly16fb88a2009-10-07 07:44:03 +0200510 }
zzy3b147b72012-04-03 19:48:32 -0700511 return b.length;
512 }
513
514 private int readInt(InputStream is) throws IOException {
515 byte[] ibytes = new byte[4];
516 int ret = readAll(is, ibytes);
Matthew Xie563e4142012-10-09 22:10:37 -0700517 if (VDBG) Log.d(TAG, "inputStream.read ret: " + ret);
zzy3b147b72012-04-03 19:48:32 -0700518 ByteBuffer bb = ByteBuffer.wrap(ibytes);
519 bb.order(ByteOrder.nativeOrder());
520 return bb.getInt();
Nick Pelly16fb88a2009-10-07 07:44:03 +0200521 }
Nick Pelly0b6955a2009-05-26 19:13:43 -0700522}