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: TestResponderServerL2CAP.java 2607 2008-12-17 23:51:33Z skarzhevskyy $
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 }