1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
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;
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
262
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
332
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 }