1 /**
2 * BlueCove - Java library for Bluetooth
3 * Copyright (C) 2004 Intel Corporation
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * @version $Id: BluetoothRFCommConnection.java 2164 2008-05-04 19:31:39Z skarzhevskyy $
20 */
21 package com.intel.bluetooth;
22
23 import java.io.DataInputStream;
24 import java.io.DataOutputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28
29 import javax.bluetooth.RemoteDevice;
30 import javax.bluetooth.ServiceRecord;
31 import javax.microedition.io.StreamConnection;
32
33 /**
34 * All StreamConnections have one underlying InputStream and one OutputStream.
35 * Opening a DataInputStream counts as opening an InputStream and opening a
36 * DataOutputStream counts as opening an OutputStream. Trying to open another
37 * InputStream or OutputStream causes an IOException. Trying to open the
38 * InputStream or OutputStream after they have been closed causes an
39 * IOException.
40 * <p>
41 * The methods of StreamConnection are not synchronized. The only stream method
42 * that can be called safely in another thread is close.
43 *
44 *
45 */
46 abstract class BluetoothRFCommConnection implements StreamConnection, BluetoothConnectionAccess {
47
48 protected BluetoothStack bluetoothStack;
49
50 protected volatile long handle;
51
52 private BluetoothRFCommInputStream in;
53
54 private BluetoothRFCommOutputStream out;
55
56 private boolean isClosed;
57
58 protected int securityOpt;
59
60 RemoteDevice remoteDevice;
61
62 protected BluetoothRFCommConnection(BluetoothStack bluetoothStack, long handle) {
63 this.bluetoothStack = bluetoothStack;
64 this.handle = handle;
65 this.isClosed = false;
66 }
67
68 abstract void closeConnectionHandle(long handle) throws IOException;
69
70 /**
71 * Close the connection.
72 * <p>
73 * Streams derived from the connection may be open when close() method is
74 * called. Any open streams will cause the connection to be held open until
75 * they themselves are closed. In this latter case access to the open
76 * streams is permitted, but access to the connection is not.
77 *
78 * @throws IOException
79 * If an I/O error occurs
80 */
81 void streamClosed() throws IOException {
82 // Closing streams does not close connection
83 if (!isClosed) {
84 return;
85 }
86
87 // Any open streams will cause the connection to be held open
88 if ((in != null) && (!in.isClosed())) {
89 return;
90 }
91
92 if ((out != null) && (!out.isClosed())) {
93 return;
94 }
95
96 shutdown();
97 }
98
99 /*
100 * (non-Javadoc)
101 *
102 * @see com.intel.bluetooth.BluetoothConnectionAccess#shutdown()
103 */
104 public void shutdown() throws IOException {
105 if (handle != 0) {
106 DebugLog.debug("closing RFCOMM Connection", handle);
107 // close() can be called safely in another thread
108 long synchronizedHandle;
109 synchronized (this) {
110 synchronizedHandle = handle;
111 handle = 0;
112 }
113 if (synchronizedHandle != 0) {
114 closeConnectionHandle(synchronizedHandle);
115 }
116 }
117 }
118
119 /**
120 * Open and return an input stream for a connection.
121 * <p>
122 * Trying to open another InputStream or OutputStream causes an IOException.
123 * <p>
124 * Trying to open the InputStream or OutputStream after they have been
125 * closed causes an IOException.
126 *
127 * @return An input stream
128 *
129 * @throws IOException
130 * If an I/O error occurs
131 */
132 public InputStream openInputStream() throws IOException {
133 if (isClosed) {
134 throw new IOException("RFCOMM Connection is already closed");
135 } else {
136 if (in == null) {
137 in = new BluetoothRFCommInputStream(this);
138 return in;
139 } else if (in.isClosed()) {
140 throw new IOException("Stream cannot be reopened");
141 } else {
142 throw new IOException("Another InputStream already opened");
143 }
144 }
145 }
146
147 /**
148 * Open and return an data input stream for a connection.
149 * <p>
150 * Opening a DataInputStream counts as opening an InputStream
151 * <p>
152 * Trying to open another InputStream or OutputStream causes an IOException.
153 * <p>
154 * Trying to open the InputStream or OutputStream after they have been
155 * closed causes an IOException.
156 *
157 * @return An input stream
158 *
159 * @throws IOException
160 * If an I/O error occurs
161 */
162 public DataInputStream openDataInputStream() throws IOException {
163 return new DataInputStream(openInputStream());
164 }
165
166 /**
167 * Open and return an output stream for a connection.
168 * <p>
169 * Trying to open another InputStream or OutputStream causes an IOException.
170 * <p>
171 * Trying to open the InputStream or OutputStream after they have been
172 * closed causes an IOException.
173 *
174 * @return An output stream
175 *
176 * @throws IOException
177 * If an I/O error occurs
178 */
179 public OutputStream openOutputStream() throws IOException {
180 if (isClosed) {
181 throw new IOException("RFCOMM Connection is already closed");
182 } else {
183 if (out == null) {
184 out = new BluetoothRFCommOutputStream(this);
185 return out;
186 } else if (out.isClosed()) {
187 throw new IOException("Stream cannot be reopened");
188 } else {
189 throw new IOException("Another OutputStream already opened");
190 }
191 }
192 }
193
194 /**
195 * Open and return an data output stream for a connection.
196 * <p>
197 * Opening a DataOutputStream counts as opening an OutputStream
198 * <p>
199 * Trying to open another InputStream or OutputStream causes an IOException.
200 * <p>
201 * Trying to open the InputStream or OutputStream after they have been
202 * closed causes an IOException.
203 *
204 * @return An output stream
205 *
206 * @throws IOException
207 * If an I/O error occurs
208 */
209 public DataOutputStream openDataOutputStream() throws IOException {
210 return new DataOutputStream(openOutputStream());
211 }
212
213 /**
214 * Close the connection.
215 * <p>
216 * When a connection has been closed, access to any of its methods except
217 * this close() will cause an an IOException to be thrown. Closing an
218 * already closed connection has no effect. Streams derived from the
219 * connection may be open when method is called. Any open streams will cause
220 * the connection to be held open until they themselves are closed. In this
221 * latter case access to the open streams is permitted, but access to the
222 * connection is not.
223 *
224 * @throws IOException
225 * If an I/O error occurs
226 */
227 public void close() throws IOException {
228 if (isClosed) {
229 return;
230 }
231 isClosed = true;
232 streamClosed();
233 }
234
235 protected void finalize() {
236 try {
237 close();
238 } catch (IOException e) {
239 }
240 }
241
242 /*
243 * (non-Javadoc)
244 *
245 * @see com.intel.bluetooth.BluetoothConnectionAccess#isClosed()
246 */
247 public boolean isClosed() {
248 return isClosed;
249 }
250
251 /*
252 * (non-Javadoc)
253 *
254 * @see com.intel.bluetooth.BluetoothConnectionAccess#markAuthenticated()
255 */
256 public void markAuthenticated() {
257 if (this.securityOpt == ServiceRecord.NOAUTHENTICATE_NOENCRYPT) {
258 this.securityOpt = ServiceRecord.AUTHENTICATE_NOENCRYPT;
259 }
260 }
261
262 /*
263 * (non-Javadoc)
264 *
265 * @see com.intel.bluetooth.BluetoothConnectionAccess#getSecurityOpt()
266 */
267 public int getSecurityOpt() {
268 try {
269 this.securityOpt = bluetoothStack.rfGetSecurityOpt(this.handle, this.securityOpt);
270 } catch (IOException notChanged) {
271 }
272 return this.securityOpt;
273 }
274
275 /*
276 * (non-Javadoc)
277 *
278 * @see com.intel.bluetooth.BluetoothConnectionAccess#encrypt(boolean)
279 */
280 public boolean encrypt(long address, boolean on) throws IOException {
281 if (isClosed) {
282 throw new IOException("RFCOMM Connection is already closed");
283 }
284 boolean changed = bluetoothStack.rfEncrypt(address, this.handle, on);
285 if (changed) {
286 if (on) {
287 this.securityOpt = ServiceRecord.AUTHENTICATE_ENCRYPT;
288 } else {
289 this.securityOpt = ServiceRecord.AUTHENTICATE_NOENCRYPT;
290 }
291 }
292 return changed;
293 }
294
295 /*
296 * (non-Javadoc)
297 *
298 * @see com.intel.bluetooth.BluetoothConnectionAccess#getRemoteAddress()
299 */
300 public long getRemoteAddress() throws IOException {
301 if (isClosed) {
302 throw new IOException("Connection closed");
303 }
304 return bluetoothStack.getConnectionRfRemoteAddress(handle);
305 }
306
307 /*
308 * (non-Javadoc)
309 *
310 * @see com.intel.bluetooth.BluetoothConnectionAccess#getRemoteDevice()
311 */
312 public RemoteDevice getRemoteDevice() {
313 return this.remoteDevice;
314 }
315
316 /*
317 * (non-Javadoc)
318 *
319 * @see com.intel.bluetooth.BluetoothConnectionAccess#setRemoteDevice(javax.bluetooth.RemoteDevice)
320 */
321 public void setRemoteDevice(RemoteDevice remoteDevice) {
322 this.remoteDevice = remoteDevice;
323 }
324
325 /*
326 * (non-Javadoc)
327 *
328 * @see com.intel.bluetooth.BluetoothConnectionAccess#getBluetoothStack()
329 */
330 public BluetoothStack getBluetoothStack() {
331 return bluetoothStack;
332 }
333 }