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