View Javadoc

1   /**
2    *  BlueCove - Java library for Bluetooth
3    *  Copyright (C) 2007-2008 Vlad Skarzhevskyy
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: OBEXSessionBase.java 2317 2008-07-03 19:22:19Z skarzhevskyy $
20   */
21  package com.intel.bluetooth.obex;
22  
23  import java.io.ByteArrayOutputStream;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.OutputStream;
27  
28  import javax.bluetooth.RemoteDevice;
29  import javax.bluetooth.ServiceRecord;
30  import javax.microedition.io.Connection;
31  import javax.microedition.io.StreamConnection;
32  import javax.obex.Authenticator;
33  import javax.obex.HeaderSet;
34  import javax.obex.ServerRequestHandler;
35  
36  import com.intel.bluetooth.BluetoothConnectionAccess;
37  import com.intel.bluetooth.BluetoothStack;
38  import com.intel.bluetooth.DebugLog;
39  
40  /**
41   * Base for Client and Server implementations. See <a
42   * href="http://bluetooth.com/Bluetooth/Learn/Technology/Specifications/">Bluetooth
43   * Specification Documents</A> for details.
44   * 
45   * @author vlads
46   * 
47   */
48  abstract class OBEXSessionBase implements Connection, BluetoothConnectionAccess {
49  
50  	protected StreamConnection conn;
51  
52  	protected InputStream is;
53  
54  	protected OutputStream os;
55  
56  	protected long connectionID;
57  
58  	protected int mtu = OBEXOperationCodes.OBEX_DEFAULT_MTU;
59  
60  	protected Authenticator authenticator;
61  
62  	protected OBEXConnectionParams obexConnectionParams;
63  
64  	protected int packetsCountWrite;
65  
66  	protected int packetsCountRead;
67  
68  	public OBEXSessionBase(StreamConnection conn, OBEXConnectionParams obexConnectionParams) throws IOException {
69  		if (obexConnectionParams == null) {
70  			throw new NullPointerException("obexConnectionParams is null");
71  		}
72  		this.conn = conn;
73  		this.obexConnectionParams = obexConnectionParams;
74  		this.mtu = obexConnectionParams.mtu;
75  		this.connectionID = -1;
76  		this.packetsCountWrite = 0;
77  		this.packetsCountRead = 0;
78  		boolean initOK = false;
79  		try {
80  			this.os = conn.openOutputStream();
81  			this.is = conn.openInputStream();
82  			initOK = true;
83  		} finally {
84  			if (!initOK) {
85  				try {
86  					this.close();
87  				} catch (IOException e) {
88  					DebugLog.error("close error", e);
89  				}
90  			}
91  		}
92  	}
93  
94  	public void close() throws IOException {
95  		StreamConnection c = this.conn;
96  		this.conn = null;
97  		try {
98  			if (this.is != null) {
99  				this.is.close();
100 				this.is = null;
101 			}
102 			if (this.os != null) {
103 				this.os.close();
104 				this.os = null;
105 			}
106 		} finally {
107 			if (c != null) {
108 				c.close();
109 			}
110 		}
111 	}
112 
113 	public static HeaderSet createOBEXHeaderSet() {
114 		return new OBEXHeaderSetImpl();
115 	}
116 
117 	static void validateCreatedHeaderSet(HeaderSet headers) {
118 		OBEXHeaderSetImpl.validateCreatedHeaderSet(headers);
119 	}
120 
121 	protected void writeOperation(int commId, byte[] data) throws IOException {
122 		writeOperation(commId, null, data);
123 	}
124 
125 	protected void writeOperation(int commId, byte[] headerFlagsData, byte[] data) throws IOException {
126 		int len = 3;
127 		if (this.connectionID != -1) {
128 			len += 5;
129 		}
130 		if (headerFlagsData != null) {
131 			len += headerFlagsData.length;
132 		}
133 		if (data != null) {
134 			len += data.length;
135 		}
136 		if (len > mtu) {
137 			throw new IOException("Can't sent more data than in MTU, len=" + len + ", mtu=" + mtu);
138 		}
139 		this.packetsCountWrite++;
140 		ByteArrayOutputStream buf = new ByteArrayOutputStream();
141 		OBEXHeaderSetImpl.writeObexLen(buf, commId, len);
142 		if (headerFlagsData != null) {
143 			buf.write(headerFlagsData);
144 		}
145 		if (this.connectionID != -1) {
146 			OBEXHeaderSetImpl.writeObexInt(buf, OBEXHeaderSetImpl.OBEX_HDR_CONNECTION, this.connectionID);
147 		}
148 		if (data != null) {
149 			buf.write(data);
150 		}
151 		DebugLog.debug0x("obex send (" + this.packetsCountWrite + ")", OBEXUtils.toStringObexResponseCodes(commId),
152 				commId);
153 		os.write(buf.toByteArray());
154 		os.flush();
155 		DebugLog.debug("obex sent (" + this.packetsCountWrite + ") len", len);
156 	}
157 
158 	protected byte[] readOperation() throws IOException {
159 		byte[] header = new byte[3];
160 		OBEXUtils.readFully(is, obexConnectionParams, header);
161 		this.packetsCountRead++;
162 		DebugLog.debug0x("obex received (" + this.packetsCountRead + ")", OBEXUtils
163 				.toStringObexResponseCodes(header[0]), header[0] & 0xFF);
164 		int lenght = OBEXUtils.bytesToShort(header[1], header[2]);
165 		if (lenght == 3) {
166 			return header;
167 		}
168 		if ((lenght < 3) || (lenght > OBEXOperationCodes.OBEX_MAX_PACKET_LEN)) {
169 			throw new IOException("Invalid packet length " + lenght);
170 		}
171 		byte[] data = new byte[lenght];
172 		System.arraycopy(header, 0, data, 0, header.length);
173 		OBEXUtils.readFully(is, obexConnectionParams, data, header.length, lenght - header.length);
174 		if (is.available() > 0) {
175 			DebugLog.debug("has more data after read", is.available());
176 		}
177 		return data;
178 	}
179 
180 	private void validateBluetoothConnection() {
181 		if ((conn != null) && !(conn instanceof BluetoothConnectionAccess)) {
182 			throw new IllegalArgumentException("Not a Bluetooth connection " + conn.getClass().getName());
183 		}
184 	}
185 
186 	void validateAuthenticationResponse(OBEXHeaderSetImpl requestHeaders, OBEXHeaderSetImpl incomingHeaders)
187 			throws IOException {
188 		if ((requestHeaders != null) && requestHeaders.hasAuthenticationChallenge()
189 				&& (!incomingHeaders.hasAuthenticationResponse())) {
190 			// TODO verify that this appropriate Exception
191 			throw new IOException("Authentication response is missing");
192 		}
193 		handleAuthenticationResponse(incomingHeaders, null);
194 	}
195 
196 	void handleAuthenticationResponse(OBEXHeaderSetImpl incomingHeaders, ServerRequestHandler serverHandler)
197 			throws IOException {
198 		if (incomingHeaders.hasAuthenticationResponse()) {
199 			if (authenticator == null) {
200 				throw new IOException("Authenticator required for authentication");
201 			}
202 			OBEXAuthentication.handleAuthenticationResponse(incomingHeaders, authenticator, serverHandler);
203 		}
204 	}
205 
206 	void handleAuthenticationChallenge(OBEXHeaderSetImpl incomingHeaders, OBEXHeaderSetImpl replyHeaders)
207 			throws IOException {
208 		if (incomingHeaders.hasAuthenticationChallenge()) {
209 			if (authenticator == null) {
210 				throw new IOException("Authenticator required for authentication");
211 			}
212 			OBEXAuthentication.handleAuthenticationChallenge(incomingHeaders, replyHeaders, authenticator);
213 		}
214 	}
215 
216 	/*
217 	 * (non-Javadoc)
218 	 * 
219 	 * @see com.intel.bluetooth.BluetoothConnectionAccess#getRemoteAddress()
220 	 */
221 	public long getRemoteAddress() throws IOException {
222 		validateBluetoothConnection();
223 		if (conn == null) {
224 			throw new IOException("Connection closed");
225 		}
226 		return ((BluetoothConnectionAccess) conn).getRemoteAddress();
227 	}
228 
229 	/*
230 	 * (non-Javadoc)
231 	 * 
232 	 * @see com.intel.bluetooth.BluetoothConnectionAccess#getRemoteDevice()
233 	 */
234 	public RemoteDevice getRemoteDevice() {
235 		validateBluetoothConnection();
236 		if (conn == null) {
237 			return null;
238 		}
239 		return ((BluetoothConnectionAccess) conn).getRemoteDevice();
240 	}
241 
242 	/*
243 	 * (non-Javadoc)
244 	 * 
245 	 * @see com.intel.bluetooth.BluetoothConnectionAccess#isClosed()
246 	 */
247 	public boolean isClosed() {
248 		if (conn == null) {
249 			return true;
250 		}
251 		if (this.conn instanceof BluetoothConnectionAccess) {
252 			return ((BluetoothConnectionAccess) conn).isClosed();
253 		} else {
254 			return false;
255 		}
256 	}
257 
258 	/*
259 	 * (non-Javadoc)
260 	 * 
261 	 * @see com.intel.bluetooth.BluetoothConnectionAccess#shutdown()
262 	 */
263 	public void shutdown() throws IOException {
264 		if (this.conn instanceof BluetoothConnectionAccess) {
265 			((BluetoothConnectionAccess) conn).shutdown();
266 		}
267 	}
268 
269 	/*
270 	 * (non-Javadoc)
271 	 * 
272 	 * @see com.intel.bluetooth.BluetoothConnectionAccess#markAuthenticated()
273 	 */
274 	public void markAuthenticated() {
275 		validateBluetoothConnection();
276 		if (conn != null) {
277 			((BluetoothConnectionAccess) conn).markAuthenticated();
278 		}
279 	}
280 
281 	/*
282 	 * (non-Javadoc)
283 	 * 
284 	 * @see com.intel.bluetooth.BluetoothConnectionAccess#getSecurityOpt()
285 	 */
286 	public int getSecurityOpt() {
287 		validateBluetoothConnection();
288 		if (conn == null) {
289 			return ServiceRecord.NOAUTHENTICATE_NOENCRYPT;
290 		} else {
291 			return ((BluetoothConnectionAccess) conn).getSecurityOpt();
292 		}
293 	}
294 
295 	/*
296 	 * (non-Javadoc)
297 	 * 
298 	 * @see com.intel.bluetooth.BluetoothConnectionAccess#encrypt(boolean)
299 	 */
300 	public boolean encrypt(long address, boolean on) throws IOException {
301 		validateBluetoothConnection();
302 		if (conn == null) {
303 			throw new IOException("Connection closed");
304 		}
305 		return ((BluetoothConnectionAccess) conn).encrypt(address, on);
306 	}
307 
308 	/*
309 	 * (non-Javadoc)
310 	 * 
311 	 * @see com.intel.bluetooth.BluetoothConnectionAccess#setRemoteDevice(javax.bluetooth.RemoteDevice)
312 	 */
313 	public void setRemoteDevice(RemoteDevice remoteDevice) {
314 		validateBluetoothConnection();
315 		if (conn != null) {
316 			((BluetoothConnectionAccess) conn).setRemoteDevice(remoteDevice);
317 		}
318 	}
319 
320 	/*
321 	 * (non-Javadoc)
322 	 * 
323 	 * @see com.intel.bluetooth.BluetoothConnectionAccess#getBluetoothStack()
324 	 */
325 	public BluetoothStack getBluetoothStack() {
326 		validateBluetoothConnection();
327 		if (conn == null) {
328 			return null;
329 		}
330 		return ((BluetoothConnectionAccess) conn).getBluetoothStack();
331 	}
332 
333 	/**
334 	 * Function used in unit tests.
335 	 * 
336 	 * @return the packetsCountWrite
337 	 */
338 	int getPacketsCountWrite() {
339 		return this.packetsCountWrite;
340 	}
341 
342 	/**
343 	 * Function used in unit tests.
344 	 * 
345 	 * @return the packetsCountRead
346 	 */
347 	int getPacketsCountRead() {
348 		return this.packetsCountRead;
349 	}
350 
351 	/**
352 	 * Function used in unit tests.
353 	 * 
354 	 * @return the mtu
355 	 */
356 	int getPacketSize() {
357 		return this.mtu;
358 	}
359 }