View Javadoc

1   /**
2    *  BlueCove - Java library for Bluetooth
3    *  Copyright (C) 2006-2008 Vlad Skarzhevskyy
4    * 
5    *  Licensed to the Apache Software Foundation (ASF) under one
6    *  or more contributor license agreements.  See the NOTICE file
7    *  distributed with this work for additional information
8    *  regarding copyright ownership.  The ASF licenses this file
9    *  to you under the Apache License, Version 2.0 (the
10   *  "License"); you may not use this file except in compliance
11   *  with the License.  You may obtain a copy of the License at
12   *
13   *    http://www.apache.org/licenses/LICENSE-2.0
14   *
15   *  Unless required by applicable law or agreed to in writing,
16   *  software distributed under the License is distributed on an
17   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18   *  KIND, either express or implied.  See the License for the
19   *  specific language governing permissions and limitations
20   *  under the License.
21   *
22   *  @author vlads
23   *  @version $Id: TestResponderServer.java 2607 2008-12-17 23:51:33Z skarzhevskyy $
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 					// Update all other working Threads
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 						// Problems on SE
382 						// updateServiceRecord();
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 		// long data;
593 		//		
594 		// Calendar calendar = Calendar.getInstance();
595 		// calendar.setTime(new Date());
596 		// data = 1 + calendar.get(Calendar.MINUTE);
597 		//        
598 		// record.setAttributeValue(Consts.VARIABLE_SERVICE_ATTRIBUTE_BYTES_ID,
599 		// new DataElement(DataElement.U_INT_4, data));
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 			// LocalDevice.getLocalDevice().updateRecord(record);
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 		// Add the service to the 'Public Browse Group'
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 }