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 net.sf.bluecove;
26
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.InterruptedIOException;
30 import java.io.OutputStream;
31 import java.util.Date;
32 import java.util.Timer;
33 import java.util.TimerTask;
34
35 import javax.bluetooth.DataElement;
36 import javax.bluetooth.LocalDevice;
37 import javax.bluetooth.RemoteDevice;
38 import javax.bluetooth.ServiceRecord;
39 import javax.bluetooth.UUID;
40 import javax.microedition.io.Connection;
41 import javax.microedition.io.Connector;
42 import javax.obex.HeaderSet;
43 import javax.obex.Operation;
44 import javax.obex.ResponseCodes;
45 import javax.obex.ServerRequestHandler;
46 import javax.obex.SessionNotifier;
47
48 import org.bluecove.tester.log.Logger;
49 import org.bluecove.tester.util.IOUtils;
50 import org.bluecove.tester.util.RuntimeDetect;
51 import org.bluecove.tester.util.StringUtils;
52 import org.bluecove.tester.util.TimeUtils;
53
54 import net.sf.bluecove.util.BluetoothTypesInfo;
55
56 public class TestResponderServerOBEX implements Runnable {
57
58 private SessionNotifier serverConnection;
59
60 private boolean isStoped = false;
61
62 private boolean isRunning = false;
63
64 private Thread thread;
65
66 private Object threadLocalBluetoothStack;
67
68 public static final UUID OBEX_OBJECT_PUSH = new UUID(0x1105);
69
70 public static final String OBEX_OBJECT_PUSH_SERVER_NAME = "OBEX Object Push";
71
72 private TestResponderServerOBEX() {
73
74 }
75
76 public static TestResponderServerOBEX startServer() {
77 TestResponderServerOBEX srv = new TestResponderServerOBEX();
78 srv.threadLocalBluetoothStack = Configuration.threadLocalBluetoothStack;
79 srv.thread = RuntimeDetect.cldcStub.createNamedThread(srv, "ServerOBEX");
80 srv.thread.start();
81 return srv;
82 }
83
84 public boolean isRunning() {
85 return isRunning;
86 }
87
88 public void run() {
89 isStoped = false;
90 boolean deviceServiceClassesUpdated = false;
91 RuntimeDetect.cldcStub.setThreadLocalBluetoothStack(threadLocalBluetoothStack);
92 LocalDevice localDevice;
93 try {
94 localDevice = LocalDevice.getLocalDevice();
95 if (Configuration.testServerOBEX_TCP.booleanValue()) {
96 serverConnection = (SessionNotifier) Connector
97 .open(BluetoothTypesInfo.PROTOCOL_SCHEME_TCP_OBEX + "://");
98 } else {
99 StringBuffer url = new StringBuffer(BluetoothTypesInfo.PROTOCOL_SCHEME_BT_OBEX);
100 url.append("://localhost:");
101 if (Configuration.testServerOBEXObjectPush) {
102 url.append(OBEX_OBJECT_PUSH);
103 url.append(";name=");
104 url.append(OBEX_OBJECT_PUSH_SERVER_NAME);
105 } else {
106 url.append(Configuration.blueCoveOBEXUUID());
107 url.append(";name=");
108 url.append(Consts.RESPONDER_SERVERNAME);
109 url.append("_ox");
110 }
111
112 url.append(Configuration.serverURLParams());
113
114 serverConnection = (SessionNotifier) Connector.open(url.toString());
115 if (Configuration.testServerOBEXObjectPush) {
116 try {
117 ServiceRecord record = localDevice.getRecord(serverConnection);
118
119 final int OBJECT_TRANSFER_SERVICE = 0x100000;
120
121 try {
122 record.setDeviceServiceClasses(OBJECT_TRANSFER_SERVICE);
123 } catch (Throwable e) {
124 Logger.debug("setDeviceServiceClasses", e);
125 }
126
127 DataElement bluetoothProfileDescriptorList = new DataElement(DataElement.DATSEQ);
128 DataElement obbexPushProfileDescriptor = new DataElement(DataElement.DATSEQ);
129 obbexPushProfileDescriptor.addElement(new DataElement(DataElement.UUID, OBEX_OBJECT_PUSH));
130 obbexPushProfileDescriptor.addElement(new DataElement(DataElement.U_INT_2, 0x100));
131 bluetoothProfileDescriptorList.addElement(obbexPushProfileDescriptor);
132 record.setAttributeValue(0x0009, bluetoothProfileDescriptorList);
133
134 final short ATTR_SUPPORTED_FORMAT_LIST_LIST = 0x0303;
135 DataElement supportedFormatList = new DataElement(DataElement.DATSEQ);
136
137 supportedFormatList.addElement(new DataElement(DataElement.U_INT_1, 0xFF));
138 record.setAttributeValue(ATTR_SUPPORTED_FORMAT_LIST_LIST, supportedFormatList);
139
140 final short UUID_PUBLICBROWSE_GROUP = 0x1002;
141 final short ATTR_BROWSE_GRP_LIST = 0x0005;
142 DataElement browseClassIDList = new DataElement(DataElement.DATSEQ);
143 UUID browseClassUUID = new UUID(UUID_PUBLICBROWSE_GROUP);
144 browseClassIDList.addElement(new DataElement(DataElement.UUID, browseClassUUID));
145 record.setAttributeValue(ATTR_BROWSE_GRP_LIST, browseClassIDList);
146
147 localDevice.updateRecord(record);
148 } catch (Throwable e) {
149 Logger.error("OBEX Service Updating SDP", e);
150 }
151 } else if (Configuration.testServiceAttributes.booleanValue()) {
152 ServiceRecord record = localDevice.getRecord(serverConnection);
153 if (record == null) {
154 Logger.warn("Bluetooth ServiceRecord is null");
155 } else {
156 TestResponderServer.buildServiceRecord(record);
157
158 try {
159 record.setDeviceServiceClasses(BluetoothTypesInfo.DeviceClassConsts.INFORMATION_SERVICE);
160 deviceServiceClassesUpdated = true;
161 } catch (Throwable e) {
162 if (e.getMessage().startsWith("Not Supported on")) {
163 Logger.error("setDeviceServiceClasses " + e.getMessage());
164 } else {
165 Logger.error("setDeviceServiceClasses", e);
166 }
167 }
168
169 try {
170 LocalDevice.getLocalDevice().updateRecord(record);
171 Logger.debug("OBEX ServiceRecord updated");
172 } catch (Throwable e) {
173 Logger.error("OBEX Service Record update error", e);
174 }
175 }
176 }
177 }
178 } catch (Throwable e) {
179 Logger.error("OBEX Server start error", e);
180 isStoped = true;
181 return;
182 }
183
184 if (deviceServiceClassesUpdated) {
185 Logger.info("DeviceClass:" + BluetoothTypesInfo.toString(localDevice.getDeviceClass()));
186 }
187
188 try {
189 int errorCount = 0;
190 int count = 0;
191 isRunning = true;
192 boolean showServiceRecordOnce = true;
193 while (!isStoped) {
194 RequestHandler handler = new RequestHandler();
195 try {
196 count++;
197 Logger.info("Accepting OBEX connections");
198 if (showServiceRecordOnce) {
199 try {
200 Logger.debug("OxUrl:"
201 + localDevice.getRecord(serverConnection).getConnectionURL(
202 Configuration.getRequiredSecurity(), false));
203 } catch (IllegalArgumentException e) {
204 Logger.debug("Can't get local serviceRecord", e);
205 }
206 showServiceRecordOnce = false;
207 }
208 if (Configuration.authenticateOBEX.getValue() != 0) {
209 handler.auth = new OBEXTestAuthenticator("server" + count);
210 handler.connectionAccepted(serverConnection.acceptAndOpen(handler, handler.auth));
211 } else {
212 handler.connectionAccepted(serverConnection.acceptAndOpen(handler));
213 }
214 } catch (InterruptedIOException e) {
215 isStoped = true;
216 break;
217 } catch (Throwable e) {
218 if (errorCount > 3) {
219 isStoped = true;
220 }
221 if (isStoped) {
222 return;
223 }
224 errorCount++;
225 Logger.error("acceptAndOpen ", e);
226 continue;
227 }
228 errorCount = 0;
229 }
230 } finally {
231 close();
232 Logger.info("OBEX Server finished! " + TimeUtils.timeNowToString());
233 isRunning = false;
234 }
235 }
236
237 void close() {
238 try {
239 if (serverConnection != null) {
240 serverConnection.close();
241 }
242 Logger.debug("OBEX ServerConnection closed");
243 } catch (Throwable e) {
244 Logger.error("OBEX Server stop error", e);
245 }
246 }
247
248 void closeServer() {
249 isStoped = true;
250 close();
251 }
252
253
254
255
256 private class NoTimeWrapper {
257
258 Object timer;
259
260 NoTimeWrapper() {
261 try {
262 timer = new Timer();
263 } catch (Throwable e) {
264 Logger.warn("OBEX Server has no timer");
265 }
266 }
267
268 void schedule(final RequestHandler handler) {
269 if (timer != null) {
270 ((Timer) timer).schedule(new TimerTask() {
271 public void run() {
272 handler.notConnectedClose();
273 }
274 }, 1000 * 30);
275 }
276 }
277
278 void cancel() {
279 if (timer != null) {
280 ((Timer) timer).cancel();
281 }
282 }
283 }
284
285 private class RequestHandler extends ServerRequestHandler {
286
287 OBEXTestAuthenticator auth;
288
289 NoTimeWrapper notConnectedTimer = new NoTimeWrapper();
290
291 boolean isConnected = false;
292
293 Connection cconn;
294
295 RemoteDevice remoteDevice;
296
297 void connectionAccepted(Connection cconn) {
298 Logger.info("Received OBEX connection");
299 this.cconn = cconn;
300 if (!Configuration.testServerOBEX_TCP.booleanValue()) {
301 try {
302 remoteDevice = RemoteDevice.getRemoteDevice(cconn);
303 Logger.debug("connected toBTAddress " + remoteDevice.getBluetoothAddress());
304 } catch (IOException e) {
305 Logger.error("OBEX Server error", e);
306 }
307 }
308 if (!isConnected) {
309 notConnectedTimer.schedule(this);
310 }
311 }
312
313 void notConnectedClose() {
314 if (!isConnected) {
315 Logger.debug("OBEX connection timeout");
316 IOUtils.closeQuietly(cconn);
317 }
318 }
319
320 private void debugHeaderSet(HeaderSet headers) {
321 if (headers == null) {
322 return;
323 }
324 try {
325 int[] headerIDArray = headers.getHeaderList();
326 if (headerIDArray == null) {
327 return;
328 }
329 Logger.debug("Headers.length:" + headerIDArray.length);
330
331 for (int i = 0; i < headerIDArray.length; i++) {
332 int hi = headerIDArray[i];
333 Object value = headers.getHeader(hi);
334 Logger.debug("h[" + hi + "]=" + value);
335 }
336 } catch (IOException e) {
337 Logger.error("headers", e);
338 }
339 }
340
341 public int onConnect(HeaderSet request, HeaderSet reply) {
342 isConnected = true;
343 notConnectedTimer.cancel();
344 Logger.debug("OBEX onConnect");
345 debugHeaderSet(request);
346 if (Configuration.authenticate.booleanValue()) {
347 if (!remoteDevice.isAuthenticated()) {
348 return ResponseCodes.OBEX_HTTP_FORBIDDEN;
349 }
350 Logger.debug("OBEX connection Authenticated");
351 }
352 return ResponseCodes.OBEX_HTTP_OK;
353 }
354
355 public void onDisconnect(HeaderSet request, HeaderSet reply) {
356 Logger.debug("OBEX onDisconnect");
357 debugHeaderSet(request);
358 }
359
360 public int onSetPath(HeaderSet request, HeaderSet reply, boolean backup, boolean create) {
361 Logger.debug("OBEX onSetPath");
362 debugHeaderSet(request);
363 return super.onSetPath(request, reply, backup, create);
364 }
365
366 public int onDelete(HeaderSet request, HeaderSet reply) {
367 Logger.debug("OBEX onDelete");
368 debugHeaderSet(request);
369 return super.onDelete(request, reply);
370 }
371
372 public int onPut(Operation op) {
373 Logger.debug("OBEX onPut");
374 try {
375 HeaderSet hs = op.getReceivedHeaders();
376 debugHeaderSet(hs);
377 String name = (String) hs.getHeader(HeaderSet.NAME);
378 if (name != null) {
379 Logger.debug("name:" + name);
380
381 HeaderSet sendHeaders = createHeaderSet();
382 sendHeaders.setHeader(HeaderSet.DESCRIPTION, name);
383 op.sendHeaders(sendHeaders);
384 }
385
386 InputStream is = op.openInputStream();
387
388 StringBuffer buf = new StringBuffer();
389 while (!isStoped) {
390 int data = is.read();
391 if (data == -1) {
392 Logger.debug("EOS recived");
393 break;
394 }
395 char c = (char) data;
396 buf.append(c);
397 if ((c == '\n') || (buf.length() > 30)) {
398 Logger.debug("cc:" + StringUtils.toBinaryText(buf));
399 buf = new StringBuffer();
400 }
401 }
402 if (buf.length() > 0) {
403 Logger.debug("cc:" + StringUtils.toBinaryText(buf));
404 }
405 op.close();
406 return ResponseCodes.OBEX_HTTP_OK;
407 } catch (IOException e) {
408 Logger.error("OBEX Server onPut error", e);
409 return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
410 } finally {
411 Logger.debug("OBEX onPut ends");
412 }
413 }
414
415 public int onGet(Operation op) {
416 Logger.debug("OBEX onGet");
417 String message = "Hello client! now " + new Date().toString();
418 try {
419 HeaderSet hs = op.getReceivedHeaders();
420 debugHeaderSet(hs);
421 String name = (String) hs.getHeader(HeaderSet.NAME);
422
423 if (name != null) {
424 message += "\nYou ask for [" + name + "]";
425 }
426 byte[] messageBytes = message.getBytes();
427 if (name != null) {
428 HeaderSet sendHeaders = createHeaderSet();
429 sendHeaders.setHeader(HeaderSet.DESCRIPTION, name);
430 sendHeaders.setHeader(HeaderSet.LENGTH, new Long(messageBytes.length));
431 op.sendHeaders(sendHeaders);
432 }
433
434 OutputStream os = op.openOutputStream();
435 os.write(messageBytes);
436 os.flush();
437 os.close();
438 op.close();
439 return ResponseCodes.OBEX_HTTP_OK;
440 } catch (IOException e) {
441 Logger.error("OBEX Server onGet error", e);
442 return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
443 } finally {
444 Logger.debug("OBEX onGet ends");
445 }
446 }
447
448 public void onAuthenticationFailure(byte[] userName) {
449 Logger.debug("OBEX AuthFailure " + new String(userName));
450 }
451
452 }
453 }