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: OBEXServerSessionImpl.java 2322 2008-07-04 03:14:54Z skarzhevskyy $
20   */
21  package com.intel.bluetooth.obex;
22  
23  import java.io.EOFException;
24  import java.io.IOException;
25  
26  import javax.microedition.io.StreamConnection;
27  import javax.obex.Authenticator;
28  import javax.obex.HeaderSet;
29  import javax.obex.ResponseCodes;
30  import javax.obex.ServerRequestHandler;
31  
32  import com.intel.bluetooth.BlueCoveImpl;
33  import com.intel.bluetooth.BluetoothServerConnection;
34  import com.intel.bluetooth.DebugLog;
35  import com.intel.bluetooth.UtilsJavaSE;
36  
37  class OBEXServerSessionImpl extends OBEXSessionBase implements Runnable, BluetoothServerConnection {
38  
39  	private ServerRequestHandler handler;
40  
41  	private boolean isConnected = false;
42  
43  	private OBEXServerOperation operation;
44  
45  	private boolean closeRequested = false;
46  
47  	private volatile boolean delayClose = false;
48  
49  	private Object canCloseEvent = new Object();
50  
51  	private Object stackID;
52  
53  	private static int threadNumber;
54  
55  	private static synchronized int nextThreadNum() {
56  		return threadNumber++;
57  	}
58  
59  	OBEXServerSessionImpl(StreamConnection connection, ServerRequestHandler handler, Authenticator authenticator,
60  			OBEXConnectionParams obexConnectionParams) throws IOException {
61  		super(connection, obexConnectionParams);
62  		this.handler = handler;
63  		this.authenticator = authenticator;
64  		stackID = BlueCoveImpl.getCurrentThreadBluetoothStackID();
65  		Thread t = new Thread(this, "OBEXServerSessionThread-" + nextThreadNum());
66  		UtilsJavaSE.threadSetDaemon(t);
67  		t.start();
68  	}
69  
70  	public void run() {
71  		try {
72  			if (stackID != null) {
73  				BlueCoveImpl.setThreadBluetoothStackID(stackID);
74  			}
75  			while (!isClosed() && !closeRequested) {
76  				if (!handleRequest()) {
77  					return;
78  				}
79  			}
80  		} catch (Throwable e) {
81  			if (this.isConnected) {
82  				DebugLog.error("OBEXServerSession error", e);
83  			}
84  		} finally {
85  			DebugLog.debug("OBEXServerSession ends");
86  			try {
87  				super.close();
88  			} catch (IOException e) {
89  				DebugLog.debug("OBEXServerSession close error", e);
90  			}
91  		}
92  	}
93  
94  	public void close() throws IOException {
95  		closeRequested = true;
96  		while (delayClose) {
97  			synchronized (canCloseEvent) {
98  				try {
99  					if (delayClose) {
100 						canCloseEvent.wait(700);
101 					}
102 				} catch (InterruptedException e) {
103 				}
104 				delayClose = false;
105 			}
106 		}
107 		if (!isClosed()) {
108 			DebugLog.debug("OBEXServerSession close");
109 			// (new Throwable()).printStackTrace();
110 			if (operation != null) {
111 				operation.close();
112 				operation = null;
113 			}
114 		}
115 		super.close();
116 	}
117 
118 	private boolean handleRequest() throws IOException {
119 		DebugLog.debug("OBEXServerSession handleRequest");
120 		delayClose = false;
121 		byte[] b;
122 		try {
123 			b = readOperation();
124 		} catch (EOFException e) {
125 			if (isConnected) {
126 				throw e;
127 			}
128 			DebugLog.debug("OBEXServerSession got EOF");
129 			close();
130 			return false;
131 		}
132 		delayClose = true;
133 		try {
134 			int opcode = b[0] & 0xFF;
135 			boolean finalPacket = ((opcode & OBEXOperationCodes.FINAL_BIT) != 0);
136 			if (finalPacket) {
137 				DebugLog.debug("OBEXServerSession got operation finalPacket");
138 			}
139 			switch (opcode) {
140 			case OBEXOperationCodes.CONNECT:
141 				processConnect(b);
142 				break;
143 			case OBEXOperationCodes.DISCONNECT:
144 				processDisconnect(b);
145 				break;
146 			case OBEXOperationCodes.PUT_FINAL:
147 			case OBEXOperationCodes.PUT:
148 				processPut(b, finalPacket);
149 				break;
150 			case OBEXOperationCodes.SETPATH | OBEXOperationCodes.FINAL_BIT:
151 			case OBEXOperationCodes.SETPATH:
152 				processSetPath(b, finalPacket);
153 				break;
154 			case OBEXOperationCodes.ABORT:
155 				processAbort();
156 				break;
157 			case OBEXOperationCodes.GET_FINAL:
158 			case OBEXOperationCodes.GET:
159 				processGet(b, finalPacket);
160 				break;
161 			default:
162 				writeOperation(ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED, null);
163 			}
164 		} finally {
165 			delayClose = false;
166 		}
167 		synchronized (canCloseEvent) {
168 			canCloseEvent.notifyAll();
169 		}
170 		return true;
171 	}
172 
173 	private void processConnect(byte[] b) throws IOException {
174 		DebugLog.debug("Connect operation");
175 		if (b[3] != OBEXOperationCodes.OBEX_VERSION) {
176 			throw new IOException("Unsupported client OBEX version " + b[3]);
177 		}
178 		if (b.length < 7) {
179 			throw new IOException("Corrupted OBEX data");
180 		}
181 		int requestedMTU = OBEXUtils.bytesToShort(b[5], b[6]);
182 		if (requestedMTU < OBEXOperationCodes.OBEX_MINIMUM_MTU) {
183 			throw new IOException("Invalid MTU " + requestedMTU);
184 		}
185 		this.mtu = requestedMTU;
186 		DebugLog.debug("mtu selected", this.mtu);
187 
188 		OBEXHeaderSetImpl requestHeaders = OBEXHeaderSetImpl.readHeaders(b, 7);
189 		handleAuthenticationResponse(requestHeaders, handler);
190 		HeaderSet replyHeaders = createOBEXHeaderSet();
191 		handleAuthenticationChallenge(requestHeaders, (OBEXHeaderSetImpl) replyHeaders);
192 		int rc = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
193 		try {
194 			rc = handler.onConnect(requestHeaders, replyHeaders);
195 		} catch (Throwable e) {
196 			DebugLog.error("onConnect", e);
197 		}
198 		byte[] connectResponse = new byte[4];
199 		connectResponse[0] = OBEXOperationCodes.OBEX_VERSION;
200 		connectResponse[1] = 0; /* Flags */
201 		connectResponse[2] = OBEXUtils.hiByte(obexConnectionParams.mtu);
202 		connectResponse[3] = OBEXUtils.loByte(obexConnectionParams.mtu);
203 		writeOperation(rc, connectResponse, OBEXHeaderSetImpl.toByteArray(replyHeaders));
204 		if (rc == ResponseCodes.OBEX_HTTP_OK) {
205 			this.isConnected = true;
206 		}
207 	}
208 
209 	private boolean validateConnection() throws IOException {
210 		if (this.isConnected) {
211 			return true;
212 		}
213 		writeOperation(ResponseCodes.OBEX_HTTP_BAD_REQUEST, null);
214 		return false;
215 	}
216 
217 	private void processDisconnect(byte[] b) throws IOException {
218 		DebugLog.debug("Disconnect operation");
219 		if (!validateConnection()) {
220 			return;
221 		}
222 		HeaderSet requestHeaders = OBEXHeaderSetImpl.readHeaders(b, 3);
223 		HeaderSet replyHeaders = createOBEXHeaderSet();
224 		int rc = ResponseCodes.OBEX_HTTP_OK;
225 		try {
226 			handler.onDisconnect(requestHeaders, replyHeaders);
227 		} catch (Throwable e) {
228 			rc = ResponseCodes.OBEX_HTTP_UNAVAILABLE;
229 			DebugLog.error("onDisconnect", e);
230 		}
231 		this.isConnected = false;
232 		writeOperation(rc, OBEXHeaderSetImpl.toByteArray(replyHeaders));
233 	}
234 
235 	private void processDelete(HeaderSet requestHeaders) throws IOException {
236 		DebugLog.debug("Delete operation");
237 		HeaderSet replyHeaders = createOBEXHeaderSet();
238 		int rc = ResponseCodes.OBEX_HTTP_OK;
239 		try {
240 			rc = handler.onDelete(requestHeaders, replyHeaders);
241 		} catch (Throwable e) {
242 			rc = ResponseCodes.OBEX_HTTP_UNAVAILABLE;
243 			DebugLog.error("onDelete", e);
244 		}
245 		writeOperation(rc, OBEXHeaderSetImpl.toByteArray(replyHeaders));
246 	}
247 
248 	private void processPut(byte[] b, boolean finalPacket) throws IOException {
249 		DebugLog.debug("Put operation");
250 		if (!validateConnection()) {
251 			return;
252 		}
253 		HeaderSet requestHeaders = OBEXHeaderSetImpl.readHeaders(b, 3);
254 		operation = new OBEXServerOperationPut(this, requestHeaders, finalPacket);
255 		while ((!finalPacket) && (!operation.isIncommingDataReceived())) {
256 			finalPacket = operation.exchangeRequestPhasePackets();
257 		}
258 		if (operation.isErrorReceived()) {
259 			return;
260 		}
261 		// A PUT operation with NO Body or End-of-Body headers whatsoever should
262 		// be treated as a delete request.
263 		if (finalPacket && (!operation.isIncommingDataReceived())) {
264 			processDelete(operation.getReceivedHeaders());
265 			return;
266 		}
267 		try {
268 			int rc = ResponseCodes.OBEX_HTTP_OK;
269 			try {
270 				rc = handler.onPut(operation);
271 			} catch (Throwable e) {
272 				rc = ResponseCodes.OBEX_HTTP_UNAVAILABLE;
273 				DebugLog.error("onPut", e);
274 			}
275 			operation.writeResponse(rc);
276 		} finally {
277 			operation.close();
278 			operation = null;
279 		}
280 	}
281 
282 	private void processGet(byte[] b, boolean finalPacket) throws IOException {
283 		DebugLog.debug("Get operation");
284 		if (!validateConnection()) {
285 			return;
286 		}
287 		HeaderSet requestHeaders = OBEXHeaderSetImpl.readHeaders(b, 3);
288 		operation = new OBEXServerOperationGet(this, requestHeaders, finalPacket);
289 		while ((!finalPacket) && (!operation.isIncommingDataReceived())) {
290 			finalPacket = operation.exchangeRequestPhasePackets();
291 		}
292 		if (operation.isErrorReceived()) {
293 			return;
294 		}
295 		try {
296 			int rc = ResponseCodes.OBEX_HTTP_OK;
297 			try {
298 				rc = handler.onGet(operation);
299 			} catch (Throwable e) {
300 				rc = ResponseCodes.OBEX_HTTP_UNAVAILABLE;
301 				DebugLog.error("onGet", e);
302 			}
303 			operation.writeResponse(rc);
304 		} finally {
305 			operation.close();
306 			operation = null;
307 		}
308 	}
309 
310 	private void processAbort() throws IOException {
311 		DebugLog.debug("Abort operation");
312 		if (!validateConnection()) {
313 			return;
314 		}
315 		if (operation != null) {
316 			operation.close();
317 			operation = null;
318 		}
319 		writeOperation(OBEXOperationCodes.OBEX_RESPONSE_SUCCESS, null);
320 	}
321 
322 	private void processSetPath(byte[] b, boolean finalPacket) throws IOException {
323 		DebugLog.debug("SetPath operation");
324 		if (!validateConnection()) {
325 			return;
326 		}
327 		if (b.length < 5) {
328 			throw new IOException("Corrupted OBEX data");
329 		}
330 		HeaderSet requestHeaders = OBEXHeaderSetImpl.readHeaders(b, 5);
331 		// DebugLog.debug("setPath b[3]", b[3]);
332 		// b[4] = (byte) ((backup?1:0) | (create?0:2));
333 		boolean backup = ((b[3] & 1) != 0);
334 		boolean create = ((b[3] & 2) == 0);
335 		DebugLog.debug("setPath backup", backup);
336 		DebugLog.debug("setPath create", create);
337 
338 		HeaderSet replyHeaders = createOBEXHeaderSet();
339 		int rc = ResponseCodes.OBEX_HTTP_OK;
340 		try {
341 			rc = handler.onSetPath(requestHeaders, replyHeaders, backup, create);
342 		} catch (Throwable e) {
343 			rc = ResponseCodes.OBEX_HTTP_UNAVAILABLE;
344 			DebugLog.error("onSetPath", e);
345 		}
346 		writeOperation(rc, OBEXHeaderSetImpl.toByteArray(replyHeaders));
347 	}
348 
349 }