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.InterruptedIOException;
29 import java.util.Enumeration;
30 import java.util.Vector;
31
32 import javax.bluetooth.L2CAPConnection;
33 import javax.bluetooth.L2CAPConnectionNotifier;
34 import javax.bluetooth.LocalDevice;
35 import javax.bluetooth.RemoteDevice;
36 import javax.bluetooth.ServiceRecord;
37 import javax.microedition.io.Connector;
38
39 import org.bluecove.tester.log.Logger;
40 import org.bluecove.tester.util.IOUtils;
41 import org.bluecove.tester.util.RuntimeDetect;
42 import org.bluecove.tester.util.TimeUtils;
43
44 import net.sf.bluecove.util.BluetoothTypesInfo;
45 import net.sf.bluecove.util.CollectionUtils;
46
47
48
49
50 public class TestResponderServerL2CAP extends Thread {
51
52 private Object threadLocalBluetoothStack;
53
54 private L2CAPConnectionNotifier serverConnection;
55
56 private boolean isStoped = false;
57
58 private boolean isRunning = false;
59
60 private Vector concurrentConnectionsThreads = new Vector();
61
62 private class ServerConnectionTread extends Thread {
63
64 L2CAPConnection channel;
65
66 boolean isRunning;
67
68 ServerConnectionTread(L2CAPConnection channel) {
69 this.channel = channel;
70 this.isRunning = true;
71 synchronized (concurrentConnectionsThreads) {
72 concurrentConnectionsThreads.addElement(this);
73 }
74 }
75
76 public void run() {
77 try {
78 receive(channel);
79 if ((!isStoped) && (isRunning)) {
80 try {
81 Thread.sleep(Configuration.serverSleepB4ClosingConnection);
82 } catch (InterruptedException e) {
83 }
84 }
85 } finally {
86 this.isRunning = false;
87 IOUtils.closeQuietly(channel);
88 synchronized (concurrentConnectionsThreads) {
89 concurrentConnectionsThreads.removeElement(this);
90 }
91 }
92 }
93
94 void shutdown() {
95 if (isRunning) {
96 IOUtils.closeQuietly(channel);
97 }
98 }
99 }
100
101 private TestResponderServerL2CAP() {
102 threadLocalBluetoothStack = Configuration.threadLocalBluetoothStack;
103 }
104
105 public static TestResponderServerL2CAP startServer() {
106 TestResponderServerL2CAP srv = new TestResponderServerL2CAP();
107 srv.start();
108 return srv;
109 }
110
111 public boolean isRunning() {
112 return isRunning;
113 }
114
115 public void run() {
116 isStoped = false;
117 try {
118 runCreateConnectionNotifier();
119 } catch (Throwable e) {
120 Logger.error("L2CAP Server start error", e);
121 isStoped = true;
122 return;
123 }
124 try {
125 runAcceptAndOpen();
126 } catch (Throwable e) {
127 Logger.error("L2CAP Server run error", e);
128 } finally {
129 close();
130 Logger.info("L2CAP Server finished! " + TimeUtils.timeNowToString());
131 isRunning = false;
132 }
133 }
134
135 private void runCreateConnectionNotifier() throws IOException {
136 RuntimeDetect.cldcStub.setThreadLocalBluetoothStack(threadLocalBluetoothStack);
137 StringBuffer url = new StringBuffer(BluetoothTypesInfo.PROTOCOL_SCHEME_L2CAP);
138 url.append("://localhost:").append(Configuration.blueCoveL2CAPUUID());
139 url.append(";name=").append(Consts.RESPONDER_SERVERNAME).append("_l2");
140 if (Configuration.useShortUUID.booleanValue()) {
141 url.append("s");
142 }
143 url.append(Configuration.serverURLParams());
144 url.append(";TransmitMTU=").append(TestResponderCommon.receiveMTU_max);
145 url.append(";ReceiveMTU=").append(TestResponderCommon.receiveMTU_max);
146 if ((RuntimeDetect.isBlueCove) && (Configuration.bluecovepsm != null)
147 && (Configuration.bluecovepsm.length() > 0)) {
148 url.append(";bluecovepsm=").append(Configuration.bluecovepsm);
149 }
150 serverConnection = (L2CAPConnectionNotifier) Connector.open(url.toString());
151 if (Configuration.testServiceAttributes.booleanValue()) {
152 ServiceRecord record = LocalDevice.getLocalDevice().getRecord(serverConnection);
153 if (record == null) {
154 Logger.warn("Bluetooth ServiceRecord is null");
155 } else {
156 TestResponderServer.buildServiceRecord(record);
157 try {
158 LocalDevice.getLocalDevice().updateRecord(record);
159 Logger.debug("L2CAP ServiceRecord updated");
160 } catch (Throwable e) {
161 Logger.error("L2CAP Service Record update error", e);
162 }
163 }
164 }
165 }
166
167 private void runAcceptAndOpen() {
168 int errorCount = 0;
169 isRunning = true;
170 boolean showServiceRecordOnce = true;
171 serviceRunLoop: while (!isStoped) {
172 L2CAPConnection channel;
173 try {
174 Logger.info("Accepting L2CAP connections");
175 if (showServiceRecordOnce) {
176 Logger.debug("L2Url:"
177 + LocalDevice.getLocalDevice().getRecord(serverConnection).getConnectionURL(
178 Configuration.getRequiredSecurity(), false));
179 showServiceRecordOnce = false;
180 }
181 channel = serverConnection.acceptAndOpen();
182 } catch (InterruptedIOException e) {
183 isStoped = true;
184 break;
185 } catch (Throwable e) {
186 Logger.error("acceptAndOpen ", e);
187 if (!(isStoped) && (errorCount > 3)) {
188 isStoped = true;
189 Logger.error("L2CAP Server stoped, too many errors");
190 }
191 if (isStoped) {
192 break serviceRunLoop;
193 }
194 errorCount++;
195 continue;
196 }
197 errorCount = 0;
198 Logger.info("Received L2CAP connection");
199 if (!Configuration.serverAcceptWhileConnected.booleanValue()) {
200 try {
201 receive(channel);
202 if (!isStoped) {
203 try {
204 Thread.sleep(Configuration.serverSleepB4ClosingConnection);
205 } catch (InterruptedException e) {
206 }
207 }
208 } finally {
209 IOUtils.closeQuietly(channel);
210 }
211 } else {
212 ServerConnectionTread t = new ServerConnectionTread(channel);
213 t.start();
214 }
215 }
216 }
217
218 void receive(L2CAPConnection channel) {
219 try {
220 int receiveLengthMax = channel.getReceiveMTU();
221 byte[] buffer = new byte[receiveLengthMax];
222
223 int receivedLength = channel.receive(buffer);
224
225 if (receivedLength == 0) {
226 Logger.debug("a zero length L2CAP packet is received");
227 } else {
228 Logger.debug("received L2CAP packet", buffer, 0, receivedLength);
229 processData(channel, buffer, receivedLength);
230 }
231
232 } catch (Throwable e) {
233 if (isStoped) {
234 return;
235 }
236 Logger.error("L2CAP receive", e);
237 }
238 }
239
240 private void processData(L2CAPConnection channel, byte[] buffer, int receivedLength) throws IOException {
241 if ((receivedLength < 3) || (buffer[0] != Consts.SEND_TEST_START)) {
242 Logger.debug("not a test client connected, will echo");
243 runEcho(channel, buffer, receivedLength);
244 return;
245 }
246 int testType = buffer[1];
247 TestStatus testStatus = new TestStatus(testType);
248 ConnectionHolderL2CAP c = new ConnectionHolderL2CAP(channel);
249
250 TestTimeOutMonitor monitorConnection = TestTimeOutMonitor.create("test" + testType, c,
251 Configuration.serverTestTimeOutSec);
252
253 byte[] initialData = new byte[receivedLength - CommunicationTesterL2CAP.INITIAL_DATA_PREFIX_LEN];
254 System.arraycopy(buffer, CommunicationTesterL2CAP.INITIAL_DATA_PREFIX_LEN, initialData, 0, receivedLength
255 - CommunicationTesterL2CAP.INITIAL_DATA_PREFIX_LEN);
256
257 try {
258 CommunicationTesterL2CAP.runTest(testType, true, c, initialData, testStatus);
259
260 TestResponderServer.countSuccess++;
261
262 Logger.debug("Test# " + testType + " " + testStatus.getName() + " ok");
263 } catch (Throwable e) {
264 if (!isStoped) {
265 TestResponderServer.failure.addFailure("test " + testType + " " + testStatus.getName(), e);
266 }
267 Logger.error("Test# " + testType + " " + testStatus.getName() + " error", e);
268 } finally {
269 monitorConnection.finish();
270 }
271 }
272
273 private void runEcho(L2CAPConnection channel, byte[] buffer, int receivedLength) throws IOException {
274 RemoteDevice device = RemoteDevice.getRemoteDevice(channel);
275 boolean authorized = false;
276 try {
277 authorized = device.isAuthorized(channel);
278 } catch (Throwable blucoveIgnoe) {
279 }
280 Logger.debug("connected:" + device.getBluetoothAddress() + (device.isAuthenticated() ? " Auth" : "")
281 + (authorized ? " Authz" : "") + (device.isEncrypted() ? " Encr" : ""));
282 Logger.debug("ReceiveMTU=" + channel.getReceiveMTU() + " TransmitMTU=" + channel.getTransmitMTU());
283
284 echo(channel, buffer, receivedLength);
285 mainLoop: while (true) {
286 while (!channel.ready()) {
287 try {
288 Thread.sleep(100);
289 } catch (InterruptedException e) {
290 break mainLoop;
291 }
292 }
293 int receiveMTU = channel.getReceiveMTU();
294 byte[] data = new byte[receiveMTU];
295 int length = channel.receive(data);
296 echo(channel, data, length);
297 }
298 }
299
300 private void echo(L2CAPConnection channel, byte[] buffer, int receivedLength) throws IOException {
301 boolean cBufHasBinary = false;
302 int messageLength = receivedLength;
303 for (int k = 0; k < receivedLength; k++) {
304 char c = (char) buffer[k];
305 if (c < ' ') {
306 if ((c == '\n') && (k == receivedLength - 1)) {
307 messageLength = receivedLength - 1;
308 break;
309 }
310 cBufHasBinary = true;
311 break;
312 }
313 }
314 String message;
315 if (messageLength != 0) {
316 message = new String(buffer, 0, messageLength);
317 } else {
318 message = "";
319 }
320 StringBuffer buf = new StringBuffer(message);
321 if (cBufHasBinary) {
322 buf.append(" [");
323 for (int k = 0; k < receivedLength; k++) {
324 buf.append(Integer.toHexString(buffer[k])).append(' ');
325 }
326 buf.append("]");
327 }
328 buf.append(" (").append(receivedLength).append(")");
329 Logger.debug("|" + buf.toString());
330
331 byte[] reply = new byte[receivedLength];
332 if (receivedLength != 0) {
333 System.arraycopy(buffer, 0, reply, 0, receivedLength);
334 }
335 channel.send(reply);
336 }
337
338 void close() {
339 try {
340 if (serverConnection != null) {
341 serverConnection.close();
342 }
343 Logger.debug("L2CAP ServerConnection closed");
344 } catch (Throwable e) {
345 Logger.error("L2CAP Server stop error", e);
346 }
347 }
348
349 void closeServer() {
350 isStoped = true;
351 close();
352 }
353
354 public void closeServerClientConnections() {
355 Vector copy = CollectionUtils.copy(concurrentConnectionsThreads);
356 for (Enumeration iter = copy.elements(); iter.hasMoreElements();) {
357 ServerConnectionTread t = (ServerConnectionTread) iter.nextElement();
358 t.shutdown();
359 }
360 }
361
362 public int countClientConnections() {
363 int count = 0;
364 synchronized (concurrentConnectionsThreads) {
365 for (Enumeration iter = concurrentConnectionsThreads.elements(); iter.hasMoreElements();) {
366 ServerConnectionTread t = (ServerConnectionTread) iter.nextElement();
367 if (t.isRunning) {
368 count++;
369 }
370 }
371 }
372 return count;
373 }
374 }