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