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.DataOutputStream;
28 import java.io.InputStream;
29 import java.io.OutputStream;
30 import java.util.Enumeration;
31 import java.util.Vector;
32
33 import javax.bluetooth.BluetoothStateException;
34 import javax.bluetooth.DataElement;
35 import javax.bluetooth.DiscoveryAgent;
36 import javax.bluetooth.LocalDevice;
37 import javax.bluetooth.RemoteDevice;
38 import javax.bluetooth.ServiceRecord;
39 import javax.bluetooth.ServiceRegistrationException;
40 import javax.bluetooth.UUID;
41 import javax.microedition.io.Connector;
42 import javax.microedition.io.StreamConnection;
43 import javax.microedition.io.StreamConnectionNotifier;
44
45 import org.bluecove.tester.log.Logger;
46 import org.bluecove.tester.util.IOUtils;
47 import org.bluecove.tester.util.RuntimeDetect;
48 import org.bluecove.tester.util.TimeUtils;
49
50 import net.sf.bluecove.se.JavaSECommon;
51 import net.sf.bluecove.util.BluetoothTypesInfo;
52 import net.sf.bluecove.util.CollectionUtils;
53 import net.sf.bluecove.util.CountStatistic;
54 import net.sf.bluecove.util.TimeStatistic;
55
56 public class TestResponderServer implements CanShutdown, Runnable {
57
58 public static int countSuccess = 0;
59
60 public static TimeStatistic allServerDuration = new TimeStatistic();
61
62 public static FailureLog failure = new FailureLog("Server failure");
63
64 public static int countConnection = 0;
65
66 public static int countRunningConnections = 0;
67
68 public static int concurrentConnectionsMax = 0;
69
70 public Thread thread;
71
72 private Object threadLocalBluetoothStack;
73
74 private long lastActivityTime;
75
76 private boolean stoped = false;
77
78 boolean isRunning = false;
79
80 public static boolean discoverable = false;
81
82 public static long discoverableStartTime = 0;
83
84 public static long connectorOpenTime = 0;
85
86 private StreamConnectionNotifier serverConnection;
87
88 private TestTimeOutMonitor monitorServer;
89
90 private TestResponderServerL2CAP responderL2CAPServerThread = null;
91
92 private TestResponderServerOBEX responderOBEXServer = null;
93
94 private Vector concurrentConnectionRunnable = new Vector();
95
96 public static CountStatistic concurrentStatistic = new CountStatistic();
97
98 public static TimeStatistic connectionDuration = new TimeStatistic();
99
100 private class ServerConnectionRunnable implements Runnable {
101
102 long connectionStartTime;
103
104 int concurrentCount = 0;
105
106 ConnectionHolderStream c = new ConnectionHolderStream();
107
108 boolean isRunning = true;
109
110 private String name;
111
112 ServerConnectionRunnable(StreamConnection conn) {
113 name = "ServerConnectionTread" + (++countConnection);
114
115 c.conn = conn;
116 connectionStartTime = System.currentTimeMillis();
117 synchronized (concurrentConnectionRunnable) {
118 concurrentConnectionRunnable.addElement(this);
119 }
120 }
121
122 String getName() {
123 return this.name;
124 }
125
126 private void concurrentNotify() {
127 synchronized (concurrentConnectionRunnable) {
128 int concurNow = concurrentConnectionRunnable.size();
129 setConcurrentCount(concurNow);
130 if (concurNow > 1) {
131
132 for (Enumeration iter = concurrentConnectionRunnable.elements(); iter.hasMoreElements();) {
133 ServerConnectionRunnable t = (ServerConnectionRunnable) iter.nextElement();
134 t.setConcurrentCount(concurNow);
135 }
136 }
137 }
138 }
139
140 private void setConcurrentCount(int concurNow) {
141 if (concurrentCount < concurNow) {
142 concurrentCount = concurNow;
143 }
144 }
145
146 private void runEcho(InputStream is, char firstChar) {
147 int receivedCount = 1;
148 StringBuffer buf = new StringBuffer();
149 char cBuf[] = new char[50];
150 int cBufIdx = 0;
151 boolean cBufHasBinary = false;
152 buf.append(firstChar);
153 cBuf[cBufIdx] = firstChar;
154 cBufIdx++;
155 try {
156 RemoteDevice device = RemoteDevice.getRemoteDevice(c.conn);
157 boolean authorized = false;
158 try {
159 authorized = device.isAuthorized(c.conn);
160 } catch (Throwable blucoveIgnoe) {
161 }
162 Logger.debug("connected:" + device.getBluetoothAddress() + (device.isAuthenticated() ? " Auth" : "")
163 + (authorized ? " Authz" : "") + (device.isEncrypted() ? " Encr" : ""));
164
165 c.os = c.conn.openOutputStream();
166 c.os.write(firstChar);
167 OutputStream os = c.os;
168 int i;
169 while ((i = is.read()) != -1) {
170 receivedCount++;
171 c.os.write(i);
172 char c = (char) i;
173 cBuf[cBufIdx] = c;
174 cBufIdx++;
175 if ((c == '\n') || (cBufIdx > 40)) {
176 if (cBufHasBinary) {
177 buf.append(" [");
178 for (int k = 0; k < cBufIdx; k++) {
179 buf.append(Integer.toHexString(cBuf[k])).append(' ');
180 }
181 buf.append("]");
182 }
183 Logger.debug("|" + buf.toString());
184 os.flush();
185 buf = new StringBuffer();
186 cBufIdx = 0;
187 cBufHasBinary = false;
188 } else {
189 buf.append(c);
190 if (c < ' ') {
191 cBufHasBinary = true;
192 }
193 }
194 }
195 } catch (Throwable e) {
196 Logger.debug("echo error", e);
197 } finally {
198 if (buf.length() != 0) {
199 Logger.debug("|" + buf.toString());
200 }
201 Logger.debug("echo received " + receivedCount);
202 }
203 }
204
205 public void run() {
206 int testType = 0;
207 TestStatus testStatus = new TestStatus();
208 TestTimeOutMonitor monitorConnection = null;
209 try {
210 c.is = c.conn.openInputStream();
211
212 countRunningConnections++;
213 concurrentNotify();
214 if (concurrentConnectionsMax < countRunningConnections) {
215 concurrentConnectionsMax = countRunningConnections;
216 Logger.info("now connected:" + countRunningConnections);
217 }
218
219 int isTest = c.is.read();
220 if (isTest == -1) {
221 Logger.debug("EOF received");
222 return;
223 }
224 if (isTest != Consts.SEND_TEST_START) {
225 Logger.debug("not a test client connected, will echo");
226 runEcho(c.is, (char) isTest);
227 return;
228 }
229 testType = c.is.read();
230 if (isTest == -1) {
231 Logger.debug("EOF received");
232 return;
233 }
234 if (testType == Consts.TEST_SERVER_TERMINATE) {
235 Logger.info("Stop requested");
236 shutdown();
237 return;
238 }
239 testStatus.setName(testType);
240 Logger.debug("run test# " + testType);
241 monitorConnection = TestTimeOutMonitor.create("test" + testType, c, Configuration.serverTestTimeOutSec);
242 c.os = c.conn.openOutputStream();
243 c.active();
244 CommunicationTester.runTest(testType, true, c, testStatus);
245 if (!testStatus.streamClosed) {
246 Logger.debug("reply OK");
247 c.active();
248 if (testStatus.replyMessage != null) {
249 c.os.write(Consts.SEND_TEST_REPLY_OK_MESSAGE);
250 c.os.write(testType);
251 DataOutputStream dos = new DataOutputStream(c.os);
252 dos.writeUTF(testStatus.replyMessage.toString());
253 dos.flush();
254 } else {
255 c.os.write(Consts.SEND_TEST_REPLY_OK);
256 c.os.write(testType);
257 }
258 c.os.flush();
259 }
260 monitorConnection.finish();
261 countSuccess++;
262 Logger.debug("Test# " + testType + " " + testStatus.getName() + " ok");
263 if (!stoped) {
264 try {
265 Thread.sleep(Configuration.serverSleepB4ClosingConnection);
266 } catch (InterruptedException e) {
267 }
268 }
269 } catch (Throwable e) {
270 if ((!stoped) && (testType < Consts.TEST_SERVER_TERMINATE)) {
271 failure.addFailure("test " + testType + " " + testStatus.getName(), e);
272 }
273 Logger.error("Test# " + testType + " " + testStatus.getName() + " error", e);
274 } finally {
275 if (monitorConnection != null) {
276 monitorConnection.finish();
277 }
278 synchronized (concurrentConnectionRunnable) {
279 concurrentConnectionRunnable.removeElement(this);
280 }
281 countRunningConnections--;
282 concurrentStatistic.add(concurrentCount);
283 connectionDuration.add(TimeUtils.since(connectionStartTime));
284 isRunning = false;
285 c.shutdown();
286 synchronized (this) {
287 notifyAll();
288 }
289 }
290 Logger.info("*Test Success:" + countSuccess + " Failure:" + failure.countFailure);
291 }
292
293 void shutdown() {
294 if (isRunning) {
295 c.shutdown();
296 }
297 }
298 }
299
300 public TestResponderServer() throws BluetoothStateException {
301 threadLocalBluetoothStack = Configuration.threadLocalBluetoothStack;
302 TestResponderCommon.startLocalDevice();
303 }
304
305 public void run() {
306 stoped = false;
307 isRunning = true;
308 if (!Configuration.serverContinuous) {
309 lastActivityTime = System.currentTimeMillis();
310 monitorServer = TestTimeOutMonitor.create("ServerUp", this, Consts.serverUpTimeOutSec);
311 }
312 RuntimeDetect.cldcStub.setThreadLocalBluetoothStack(threadLocalBluetoothStack);
313 try {
314 LocalDevice localDevice = LocalDevice.getLocalDevice();
315 try {
316 if ((localDevice.getDiscoverable() == DiscoveryAgent.NOT_DISCOVERABLE)
317 || (Configuration.testServerForceDiscoverable.booleanValue())) {
318 setDiscoverable();
319 }
320 } catch (Throwable ignore) {
321 Logger.warn("Error: " + ignore.toString());
322 }
323
324 if (Configuration.testRFCOMM.booleanValue()) {
325
326 StringBuffer url = new StringBuffer(BluetoothTypesInfo.PROTOCOL_SCHEME_RFCOMM);
327 url.append("://localhost:").append(Configuration.blueCoveUUID());
328 url.append(";name=").append(Consts.RESPONDER_SERVERNAME).append("_rf");
329 if (Configuration.useShortUUID.booleanValue()) {
330 url.append("s");
331 }
332 url.append(Configuration.serverURLParams());
333
334 serverConnection = (StreamConnectionNotifier) Connector.open(url.toString());
335
336 connectorOpenTime = System.currentTimeMillis();
337 Logger.info("ResponderServer started " + TimeUtils.timeNowToString());
338 if (Configuration.testServiceAttributes.booleanValue()) {
339 ServiceRecord record = LocalDevice.getLocalDevice().getRecord(serverConnection);
340 if (record == null) {
341 Logger.warn("Bluetooth ServiceRecord is null");
342 } else {
343 String initial = BluetoothTypesInfo.toString(record);
344 boolean printAllVersion = true;
345 if (printAllVersion) {
346 Logger.debug("ServiceRecord\n" + initial);
347 }
348 buildServiceRecord(record);
349 try {
350 localDevice.updateRecord(record);
351 Logger.debug("ServiceRecord updated\n" + BluetoothTypesInfo.toString(record));
352 } catch (Throwable e) {
353 if (!printAllVersion) {
354 Logger.debug("ServiceRecord\n" + initial);
355 }
356 Logger.error("Service Record update error", e);
357 }
358 }
359 }
360 }
361
362 if (Configuration.supportL2CAP) {
363 if (Configuration.testL2CAP.booleanValue()) {
364 responderL2CAPServerThread = TestResponderServerL2CAP.startServer();
365 }
366 } else {
367 Logger.info("No L2CAP support");
368 }
369 if (Configuration.testRFCOMM.booleanValue()) {
370 try {
371 responderOBEXServer = TestResponderServerOBEX.startServer();
372 } catch (Throwable noObex) {
373 Logger.error("OBEX Service ", noObex);
374 }
375 }
376
377 if (Configuration.testRFCOMM.booleanValue()) {
378 boolean showServiceRecordOnce = true;
379 while ((Configuration.testRFCOMM.booleanValue()) && (!stoped)) {
380 if ((countConnection % 5 == 0) && (Configuration.testServiceAttributes.booleanValue())) {
381
382
383 }
384 Logger.info("Accepting RFCOMM connections");
385 if (showServiceRecordOnce) {
386 Logger.debug("RfUrl:"
387 + LocalDevice.getLocalDevice().getRecord(serverConnection).getConnectionURL(
388 Configuration.getRequiredSecurity(), false));
389 }
390 StreamConnection conn = serverConnection.acceptAndOpen();
391 if (!stoped) {
392 Logger.info("Received RFCOMM connection");
393 if (countConnection % 5 == 0) {
394 Logger.debug("Server up time " + TimeUtils.secSince(connectorOpenTime));
395 Logger.debug("max concurrent con " + concurrentConnectionsMax);
396 }
397 if (showServiceRecordOnce) {
398 Logger.debug("ServiceRecord\n"
399 + BluetoothTypesInfo.toString(LocalDevice.getLocalDevice().getRecord(
400 serverConnection)));
401 showServiceRecordOnce = false;
402 }
403 lastActivityTime = System.currentTimeMillis();
404 ServerConnectionRunnable r = new ServerConnectionRunnable(conn);
405 Thread t = RuntimeDetect.cldcStub.createNamedThread(r, r.getName());
406 t.start();
407 if (!Configuration.serverAcceptWhileConnected.booleanValue()) {
408 while (r.isRunning) {
409 synchronized (r) {
410 try {
411 r.wait();
412 } catch (InterruptedException e) {
413 break;
414 }
415 }
416 }
417 }
418 } else {
419 IOUtils.closeQuietly(conn);
420 }
421 Switcher.yield(this);
422 }
423 closeServer();
424 }
425
426 } catch (Throwable e) {
427 if (!stoped) {
428 Logger.error("RFCOMM Server start error", e);
429 }
430 } finally {
431 Logger.info("RFCOMM Server finished! " + TimeUtils.timeNowToString());
432 isRunning = false;
433 }
434 if (monitorServer != null) {
435 monitorServer.finish();
436 }
437 }
438
439 public boolean isRunning() {
440 return isRunning || ((responderL2CAPServerThread != null) && responderL2CAPServerThread.isRunning())
441 || ((responderOBEXServer != null) && (responderOBEXServer.isRunning()));
442 }
443
444 public static long avgServerDurationSec() {
445 return allServerDuration.avgSec();
446 }
447
448 public boolean hasRunningConnections() {
449 return (countRunningConnections > 0);
450 }
451
452 public long lastActivityTime() {
453 return lastActivityTime;
454
455 }
456
457 public static void clear() {
458 countSuccess = 0;
459 countConnection = 0;
460 concurrentConnectionsMax = 0;
461 allServerDuration.clear();
462 failure.clear();
463 concurrentStatistic.clear();
464 connectionDuration.clear();
465 }
466
467 private void closeServer() {
468 closeServerClientConnections();
469 if (serverConnection != null) {
470 synchronized (this) {
471 try {
472 if (serverConnection != null) {
473 serverConnection.close();
474 }
475 Logger.debug("serverConnection closed");
476 } catch (Throwable e) {
477 Logger.error("Server stop error", e);
478 }
479 }
480 serverConnection = null;
481 }
482 TestResponderServerL2CAP t = responderL2CAPServerThread;
483 responderL2CAPServerThread = null;
484 if (t != null) {
485 t.closeServer();
486 }
487 if (responderOBEXServer != null) {
488 responderOBEXServer.closeServer();
489 responderOBEXServer = null;
490 }
491 setNotDiscoverable();
492 }
493
494 public void closeServerClientConnections() {
495 Vector copy = CollectionUtils.copy(concurrentConnectionRunnable);
496 for (Enumeration iter = copy.elements(); iter.hasMoreElements();) {
497 ServerConnectionRunnable t = (ServerConnectionRunnable) iter.nextElement();
498 t.shutdown();
499 }
500 if (responderL2CAPServerThread != null) {
501 responderL2CAPServerThread.closeServerClientConnections();
502 }
503 }
504
505 public int countClientConnections() {
506 int count = 0;
507 synchronized (concurrentConnectionRunnable) {
508 for (Enumeration iter = concurrentConnectionRunnable.elements(); iter.hasMoreElements();) {
509 ServerConnectionRunnable t = (ServerConnectionRunnable) iter.nextElement();
510 if (t.isRunning) {
511 count++;
512 }
513 }
514 }
515 if (responderL2CAPServerThread != null) {
516 count += responderL2CAPServerThread.countClientConnections();
517 }
518 return count;
519 }
520
521 public static boolean setDiscoverable() {
522 return setDiscoverable(DiscoveryAgent.GIAC);
523 }
524
525 public static String discoverableModeString(int mode) {
526 if (DiscoveryAgent.GIAC == mode) {
527 return "GIAC";
528 } else if (DiscoveryAgent.LIAC == mode) {
529 return "LIAC";
530 } else if (DiscoveryAgent.NOT_DISCOVERABLE == mode) {
531 return "NOT_DISCOVERABLE";
532 } else {
533 return "0x" + Integer.toHexString(mode);
534 }
535 }
536
537 public static boolean setDiscoverable(int mode) {
538 try {
539 LocalDevice localDevice = LocalDevice.getLocalDevice();
540 boolean rc = localDevice.setDiscoverable(mode);
541 if (!rc) {
542 Logger.error("Set Discoverable " + discoverableModeString(mode) + " " + rc);
543 } else {
544 Logger.debug("Set Discoverable " + discoverableModeString(mode) + " " + rc);
545 }
546 discoverable = true;
547 discoverableStartTime = System.currentTimeMillis();
548 return true;
549 } catch (Throwable e) {
550 Logger.error("Set Discoverable error", e);
551 return false;
552 }
553 }
554
555 public static void setNotDiscoverable() {
556 try {
557 allServerDuration.add(TimeUtils.since(discoverableStartTime));
558 LocalDevice localDevice = LocalDevice.getLocalDevice();
559 localDevice.setDiscoverable(DiscoveryAgent.NOT_DISCOVERABLE);
560 Logger.debug("Set Not Discoverable");
561 discoverable = false;
562 } catch (Throwable e) {
563 Logger.error("set NotDiscoverable error", e);
564 }
565 }
566
567 public void shutdown() {
568 Logger.info("shutdownServer");
569 stoped = true;
570 if (RuntimeDetect.cldcStub != null) {
571 RuntimeDetect.cldcStub.interruptThread(thread);
572 }
573 closeServer();
574 }
575
576 public void updateServiceRecord() {
577 if (serverConnection == null) {
578 return;
579 }
580 try {
581 ServiceRecord record = LocalDevice.getLocalDevice().getRecord(serverConnection);
582 if (record != null) {
583 updateVariableServiceRecord(record);
584 LocalDevice.getLocalDevice().updateRecord(record);
585 }
586 } catch (Throwable e) {
587 Logger.error("updateServiceRecord", e);
588 }
589 }
590
591 static void updateVariableServiceRecord(ServiceRecord record) {
592
593
594
595
596
597
598
599
600 }
601
602 static void buildServiceRecord(ServiceRecord record) throws ServiceRegistrationException {
603 String id = "";
604 try {
605 if (Configuration.useServiceClassExtUUID.booleanValue()) {
606 DataElement serviceClassIDList = record.getAttributeValue(BluetoothTypesInfo.ServiceClassIDList);
607 if (serviceClassIDList == null) {
608 serviceClassIDList = new DataElement(DataElement.DATSEQ);
609 }
610 serviceClassIDList.addElement(new DataElement(DataElement.UUID, Consts.uuidSrvClassExt));
611 setAttributeValue(record, BluetoothTypesInfo.ServiceClassIDList, serviceClassIDList);
612 }
613
614 if (Configuration.testAllServiceAttributes.booleanValue()) {
615 id = "all";
616 ServiceRecordTester.addAllTestServiceAttributes(record);
617 return;
618 }
619
620 id = "pub";
621 buildServiceRecordPub(record);
622 id = "int";
623 record.setAttributeValue(Consts.TEST_SERVICE_ATTRIBUTE_INT_ID, new DataElement(
624 Consts.TEST_SERVICE_ATTRIBUTE_INT_TYPE, Consts.TEST_SERVICE_ATTRIBUTE_INT_VALUE));
625 id = "long";
626 record.setAttributeValue(Consts.TEST_SERVICE_ATTRIBUTE_LONG_ID, new DataElement(
627 Consts.TEST_SERVICE_ATTRIBUTE_LONG_TYPE, Consts.TEST_SERVICE_ATTRIBUTE_LONG_VALUE));
628 if (!Configuration.testIgnoreNotWorkingServiceAttributes.booleanValue()) {
629 id = "str";
630 record.setAttributeValue(Consts.TEST_SERVICE_ATTRIBUTE_STR_ID, new DataElement(DataElement.STRING,
631 Consts.TEST_SERVICE_ATTRIBUTE_STR_VALUE));
632 }
633 id = "url";
634 record.setAttributeValue(Consts.TEST_SERVICE_ATTRIBUTE_URL_ID, new DataElement(DataElement.URL,
635 Consts.TEST_SERVICE_ATTRIBUTE_URL_VALUE));
636
637 id = "bytes";
638 record.setAttributeValue(Consts.TEST_SERVICE_ATTRIBUTE_BYTES_ID, new DataElement(
639 Consts.TEST_SERVICE_ATTRIBUTE_BYTES_TYPE, Consts.TEST_SERVICE_ATTRIBUTE_BYTES_VALUE));
640
641 id = "variable";
642 updateVariableServiceRecord(record);
643
644 id = "info";
645 record.setAttributeValue(Consts.SERVICE_ATTRIBUTE_BYTES_SERVER_INFO, new DataElement(DataElement.URL,
646 ServiceRecordTester.getBTSystemInfo()));
647
648 id = "update";
649
650
651 } catch (Throwable e) {
652 Logger.error("ServiceRecord " + id, e);
653 }
654 }
655
656 static void setAttributeValue(ServiceRecord record, int attrID, DataElement attrValue) {
657 try {
658 if (!record.setAttributeValue(attrID, attrValue)) {
659 Logger.error("SrvReg attrID=" + attrID);
660 }
661 } catch (Exception e) {
662 Logger.error("SrvReg attrID=" + attrID, e);
663 }
664 }
665
666 static void buildServiceRecordPub(ServiceRecord record) throws ServiceRegistrationException {
667 final short UUID_PUBLICBROWSE_GROUP = 0x1002;
668 final short ATTR_BROWSE_GRP_LIST = 0x0005;
669
670 DataElement browseClassIDList = new DataElement(DataElement.DATSEQ);
671 UUID browseClassUUID = new UUID(UUID_PUBLICBROWSE_GROUP);
672 browseClassIDList.addElement(new DataElement(DataElement.UUID, browseClassUUID));
673 setAttributeValue(record, ATTR_BROWSE_GRP_LIST, browseClassIDList);
674 }
675
676 public static void main(String[] args) {
677 JavaSECommon.initOnce();
678 try {
679 (new TestResponderServer()).run();
680 if (TestResponderServer.failure.countFailure > 0) {
681 System.exit(1);
682 } else {
683 System.exit(0);
684 }
685 } catch (Throwable e) {
686 Logger.error("start error ", e);
687 System.exit(1);
688 }
689 }
690
691 }