1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package com.intel.bluetooth;
23
24 import java.io.DataInputStream;
25 import java.io.DataOutputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.OutputStream;
29 import java.util.Enumeration;
30 import java.util.Hashtable;
31
32 import javax.bluetooth.BluetoothConnectionException;
33 import javax.bluetooth.L2CAPConnection;
34 import javax.bluetooth.UUID;
35 import javax.microedition.io.Connection;
36 import javax.microedition.io.ConnectionNotFoundException;
37 import javax.microedition.io.Connector;
38 import javax.microedition.io.InputConnection;
39 import javax.microedition.io.OutputConnection;
40
41 import com.intel.bluetooth.gcf.socket.ServerSocketConnection;
42 import com.intel.bluetooth.gcf.socket.SocketConnection;
43 import com.intel.bluetooth.obex.OBEXClientSessionImpl;
44 import com.intel.bluetooth.obex.OBEXConnectionParams;
45 import com.intel.bluetooth.obex.OBEXSessionNotifierImpl;
46
47
48
49
50
51
52
53
54
55
56
57 public abstract class MicroeditionConnector {
58
59
60
61
62 public static final int READ = Connector.READ;
63
64
65
66
67
68 public static final int WRITE = Connector.WRITE;
69
70
71
72
73
74 public static final int READ_WRITE = Connector.READ_WRITE;
75
76 private static Hashtable
77
78 private static Hashtable
79
80 private static Hashtable
81
82 private static Hashtable
83
84 private static Hashtable
85
86 private static final String AUTHENTICATE = "authenticate";
87
88 private static final String AUTHORIZE = "authorize";
89
90 private static final String ENCRYPT = "encrypt";
91
92 private static final String MASTER = "master";
93
94 private static final String NAME = "name";
95
96 private static final String RECEIVE_MTU = "receivemtu";
97
98 private static final String TRANSMIT_MTU = "transmitmtu";
99
100 private static final String EXT_BLUECOVE_L2CAP_PSM = "bluecovepsm";
101
102 static {
103
104 cliParams.put(AUTHENTICATE, AUTHENTICATE);
105 cliParams.put(ENCRYPT, ENCRYPT);
106 cliParams.put(MASTER, MASTER);
107
108
109 copyAll(srvParams, cliParams);
110 srvParams.put(AUTHORIZE, AUTHORIZE);
111 srvParams.put(NAME, NAME);
112
113 copyAll(cliParamsL2CAP, cliParams);
114
115 cliParamsL2CAP.put(RECEIVE_MTU, RECEIVE_MTU);
116 cliParamsL2CAP.put(TRANSMIT_MTU, TRANSMIT_MTU);
117
118 copyAll(srvParamsL2CAP, cliParamsL2CAP);
119 srvParamsL2CAP.put(AUTHORIZE, AUTHORIZE);
120 srvParamsL2CAP.put(NAME, NAME);
121 srvParamsL2CAP.put(EXT_BLUECOVE_L2CAP_PSM, EXT_BLUECOVE_L2CAP_PSM);
122
123
124
125
126 suportScheme.put(BluetoothConsts.PROTOCOL_SCHEME_RFCOMM, Boolean.TRUE);
127 suportScheme.put(BluetoothConsts.PROTOCOL_SCHEME_BT_OBEX, Boolean.TRUE);
128 suportScheme.put(BluetoothConsts.PROTOCOL_SCHEME_TCP_OBEX, Boolean.TRUE);
129 suportScheme.put(BluetoothConsts.PROTOCOL_SCHEME_L2CAP, Boolean.TRUE);
130 suportScheme.put("socket", Boolean.TRUE);
131 }
132
133 private MicroeditionConnector() {
134
135 }
136
137 static void copyAll(Hashtable dest, Hashtable src) {
138 for (Enumeration en = src.keys(); en.hasMoreElements();) {
139 Object key = en.nextElement();
140 dest.put(key, src.get(key));
141 }
142 }
143
144 static String validParamName(Hashtable map, String paramName) {
145 String validName = (String) map.get(paramName.toLowerCase());
146 if (validName != null) {
147 return validName;
148 }
149 return null;
150 }
151
152
153
154
155
156
157
158
159
160
161
162 public static Connection open(String name) throws IOException {
163 return openImpl(name, READ_WRITE, false, true);
164 }
165
166 private static Connection openImpl(String name, int mode, boolean timeouts, boolean allowServer) throws IOException {
167
168 DebugLog.debug("connecting", name);
169
170
171
172
173
174 String host = null;
175 String portORuuid = null;
176
177 Hashtable values = new Hashtable();
178
179
180 int schemeEnd = name.indexOf("://");
181 if (schemeEnd == -1) {
182 throw new ConnectionNotFoundException(name);
183 }
184 String scheme = name.substring(0, schemeEnd);
185 if (!suportScheme.containsKey(scheme)) {
186 throw new ConnectionNotFoundException(scheme);
187 }
188 boolean schemeBluetooth = (scheme.equals(BluetoothConsts.PROTOCOL_SCHEME_RFCOMM))
189 || (scheme.equals(BluetoothConsts.PROTOCOL_SCHEME_BT_OBEX) || (scheme
190 .equals(BluetoothConsts.PROTOCOL_SCHEME_L2CAP)));
191 boolean isL2CAP = scheme.equals(BluetoothConsts.PROTOCOL_SCHEME_L2CAP);
192 boolean isTCPOBEX = scheme.equals(BluetoothConsts.PROTOCOL_SCHEME_TCP_OBEX);
193
194 BluetoothStack bluetoothStack = null;
195
196 if (schemeBluetooth) {
197 bluetoothStack = BlueCoveImpl.instance().getBluetoothStack();
198 }
199
200 boolean isServer;
201
202 int hostEnd = name.indexOf(':', scheme.length() + 3);
203
204 if (hostEnd > -1) {
205 host = name.substring(scheme.length() + 3, hostEnd);
206 isServer = host.equals("localhost");
207
208 Hashtable params;
209 if (isTCPOBEX) {
210 params = new Hashtable();
211 isServer = (host.length() == 0);
212 } else if (isL2CAP) {
213 if (isServer) {
214 params = srvParamsL2CAP;
215 } else {
216 params = cliParamsL2CAP;
217 }
218 } else {
219 if (isServer) {
220 params = srvParams;
221 } else {
222 params = cliParams;
223 }
224 }
225
226 String paramsStr = name.substring(hostEnd + 1);
227 UtilsStringTokenizer tok = new UtilsStringTokenizer(paramsStr, ";");
228 if (tok.hasMoreTokens()) {
229 portORuuid = tok.nextToken();
230 } else {
231 portORuuid = paramsStr;
232 }
233 while (tok.hasMoreTokens()) {
234 String t = tok.nextToken();
235 int equals = t.indexOf('=');
236 if (equals > -1) {
237 String param = t.substring(0, equals);
238 String value = t.substring(equals + 1);
239 String validName = validParamName(params, param);
240 if (validName != null) {
241 String hasValue = (String) values.get(validName);
242 if ((hasValue != null) && (!hasValue.equals(value))) {
243 throw new IllegalArgumentException("duplicate param [" + param + "] value [" + value + "]");
244 }
245 values.put(validName, value);
246 } else {
247 throw new IllegalArgumentException("invalid param [" + param + "] value [" + value + "]");
248 }
249 } else {
250 throw new IllegalArgumentException("invalid param [" + t + "]");
251 }
252 }
253 } else if (isTCPOBEX) {
254 host = name.substring(scheme.length() + 3);
255 isServer = (host.length() == 0);
256 } else {
257 throw new IllegalArgumentException(name.substring(scheme.length() + 3));
258 }
259
260 if (isTCPOBEX) {
261 if ((portORuuid == null) || (portORuuid.length() == 0)) {
262 portORuuid = String.valueOf(BluetoothConsts.TCP_OBEX_DEFAULT_PORT);
263 }
264
265
266
267
268
269
270
271
272
273
274
275
276 }
277
278 if (host == null || portORuuid == null) {
279 throw new IllegalArgumentException();
280 }
281
282 BluetoothConnectionNotifierParams notifierParams = null;
283
284 BluetoothConnectionParams connectionParams = null;
285
286 int channel = 0;
287 if (isServer) {
288 if (!allowServer) {
289 throw new IllegalArgumentException("Can't use server connection URL");
290 }
291 if (values.get(NAME) == null) {
292 values.put(NAME, "BlueCove");
293 } else if (schemeBluetooth) {
294 validateBluetoothServiceName((String) values.get(NAME));
295 }
296 if (schemeBluetooth) {
297 notifierParams = new BluetoothConnectionNotifierParams(new UUID(portORuuid, false), paramBoolean(
298 values, AUTHENTICATE), paramBoolean(values, ENCRYPT), paramBoolean(values, AUTHORIZE),
299 (String) values.get(NAME), paramBoolean(values, MASTER));
300 notifierParams.timeouts = timeouts;
301 if (notifierParams.encrypt && (!notifierParams.authenticate)) {
302 if (values.get(AUTHENTICATE) == null) {
303 notifierParams.authenticate = true;
304 } else {
305 throw new BluetoothConnectionException(BluetoothConnectionException.UNACCEPTABLE_PARAMS,
306 "encryption requires authentication");
307 }
308 }
309 if (notifierParams.authorize && (!notifierParams.authenticate)) {
310 if (values.get(AUTHENTICATE) == null) {
311 notifierParams.authenticate = true;
312 } else {
313 throw new BluetoothConnectionException(BluetoothConnectionException.UNACCEPTABLE_PARAMS,
314 "authorization requires authentication");
315 }
316 }
317 if (isL2CAP) {
318 String bluecove_ext_psm = (String) values.get(EXT_BLUECOVE_L2CAP_PSM);
319 if (bluecove_ext_psm != null) {
320 int psm = Integer.parseInt(bluecove_ext_psm, 16);
321 validateL2CAPPSM(psm, bluecove_ext_psm);
322 notifierParams.bluecove_ext_psm = psm;
323 }
324 }
325 }
326 } else {
327 try {
328 channel = Integer.parseInt(portORuuid, isL2CAP ? 16 : 10);
329 } catch (NumberFormatException e) {
330 throw new IllegalArgumentException("channel " + portORuuid);
331 }
332 if (channel < 0) {
333 throw new IllegalArgumentException("channel " + portORuuid);
334 }
335 if (schemeBluetooth) {
336 if (isL2CAP) {
337 validateL2CAPPSM(channel, portORuuid);
338 } else {
339 if ((channel < BluetoothConsts.RFCOMM_CHANNEL_MIN)
340 || (channel > BluetoothConsts.RFCOMM_CHANNEL_MAX)) {
341 throw new IllegalArgumentException("RFCOMM channel " + portORuuid);
342 }
343 }
344
345 connectionParams = new BluetoothConnectionParams(RemoteDeviceHelper.getAddress(host), channel,
346 paramBoolean(values, AUTHENTICATE), paramBoolean(values, ENCRYPT));
347 connectionParams.timeouts = timeouts;
348 if (connectionParams.encrypt && (!connectionParams.authenticate)) {
349 if (values.get(AUTHENTICATE) == null) {
350 connectionParams.authenticate = true;
351 } else {
352 throw new BluetoothConnectionException(BluetoothConnectionException.UNACCEPTABLE_PARAMS,
353 "encryption requires authentication");
354 }
355 }
356 String timeout = BlueCoveImpl.getConfigProperty(BlueCoveConfigProperties.PROPERTY_CONNECT_TIMEOUT);
357 if (timeout != null) {
358 connectionParams.timeout = Integer.parseInt(timeout);
359 }
360 }
361 }
362 OBEXConnectionParams obexConnectionParams = null;
363
364 if (scheme.equals(BluetoothConsts.PROTOCOL_SCHEME_TCP_OBEX)
365 || scheme.equals(BluetoothConsts.PROTOCOL_SCHEME_BT_OBEX)) {
366 obexConnectionParams = new OBEXConnectionParams();
367 obexConnectionParams.timeouts = timeouts;
368 String timeout = BlueCoveImpl.getConfigProperty(BlueCoveConfigProperties.PROPERTY_OBEX_TIMEOUT);
369 if (timeout != null) {
370 obexConnectionParams.timeout = Integer.parseInt(timeout);
371 }
372 String mtu = BlueCoveImpl.getConfigProperty(BlueCoveConfigProperties.PROPERTY_OBEX_MTU);
373 if (mtu != null) {
374 obexConnectionParams.mtu = Integer.parseInt(mtu);
375 }
376 }
377
378
379
380
381 if (scheme.equals(BluetoothConsts.PROTOCOL_SCHEME_RFCOMM)) {
382 if (isServer) {
383 return new BluetoothRFCommConnectionNotifier(bluetoothStack, notifierParams);
384 } else {
385 return new BluetoothRFCommClientConnection(bluetoothStack, connectionParams);
386 }
387 } else if (scheme.equals(BluetoothConsts.PROTOCOL_SCHEME_BT_OBEX)) {
388 if (isServer) {
389 notifierParams.obex = true;
390 return new OBEXSessionNotifierImpl(
391 new BluetoothRFCommConnectionNotifier(bluetoothStack, notifierParams), obexConnectionParams);
392 } else {
393 return new OBEXClientSessionImpl(new BluetoothRFCommClientConnection(bluetoothStack, connectionParams),
394 obexConnectionParams);
395 }
396 } else if (scheme.equals(BluetoothConsts.PROTOCOL_SCHEME_L2CAP)) {
397 if (isServer) {
398 return new BluetoothL2CAPConnectionNotifier(bluetoothStack, notifierParams, paramL2CAPMTU(values,
399 RECEIVE_MTU), paramL2CAPMTU(values, TRANSMIT_MTU));
400 } else {
401 return new BluetoothL2CAPClientConnection(bluetoothStack, connectionParams, paramL2CAPMTU(values,
402 RECEIVE_MTU), paramL2CAPMTU(values, TRANSMIT_MTU));
403 }
404 } else if (scheme.equals(BluetoothConsts.PROTOCOL_SCHEME_TCP_OBEX)) {
405 if (isServer) {
406 try {
407 channel = Integer.parseInt(portORuuid);
408 } catch (NumberFormatException e) {
409 throw new IllegalArgumentException("port " + portORuuid);
410 }
411 return new OBEXSessionNotifierImpl(new ServerSocketConnection(channel), obexConnectionParams);
412 } else {
413 return new OBEXClientSessionImpl(new SocketConnection(host, channel), obexConnectionParams);
414 }
415 } else if (scheme.equals("socket")) {
416 if (isServer) {
417 try {
418 channel = Integer.parseInt(portORuuid);
419 } catch (NumberFormatException e) {
420 throw new IllegalArgumentException("port " + portORuuid);
421 }
422 return new ServerSocketConnection(channel);
423 } else {
424 return new SocketConnection(host, channel);
425 }
426 } else {
427 throw new ConnectionNotFoundException(scheme);
428 }
429 }
430
431 private static void validateL2CAPPSM(int channel, String channelAsString) throws IllegalArgumentException {
432
433
434 if ((channel < BluetoothConsts.L2CAP_PSM_MIN) || (channel > BluetoothConsts.L2CAP_PSM_MAX)) {
435
436 throw new IllegalArgumentException("PCM " + channelAsString);
437 }
438
439 if ((channel & 0x100) != 0) {
440 throw new IllegalArgumentException("9th bit set in PCM " + channelAsString);
441 }
442 if ((channel % 2) == 0) {
443 throw new IllegalArgumentException("PSM value " + channelAsString + " should be odd");
444 }
445 }
446
447 private static void validateBluetoothServiceName(String serviceName) {
448 if (serviceName.length() == 0) {
449 throw new IllegalArgumentException("zero length service name");
450 }
451 final String allowNameCharactes = " -_";
452 for (int i = 0; i < serviceName.length(); i++) {
453 char c = serviceName.charAt(i);
454 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')
455 || allowNameCharactes.indexOf(c) != -1) {
456 continue;
457 }
458 throw new IllegalArgumentException("Illegal character '" + c + "' in service name");
459 }
460 }
461
462 private static boolean paramBoolean(Hashtable values, String name) {
463 String v = (String) values.get(name);
464 if (v == null) {
465 return false;
466 } else if ("true".equals(v)) {
467 return true;
468 } else if ("false".equals(v)) {
469 return false;
470 } else {
471 throw new IllegalArgumentException("invalid param value " + name + "=" + v);
472 }
473 }
474
475 private static int paramL2CAPMTU(Hashtable values, String name) {
476 String v = (String) values.get(name);
477 if (v == null) {
478 if (name.equals(TRANSMIT_MTU)) {
479
480 return -1;
481 } else {
482 return L2CAPConnection.DEFAULT_MTU;
483 }
484 }
485 try {
486 int mtu = Integer.parseInt(v);
487 if (mtu >= L2CAPConnection.MINIMUM_MTU) {
488 return mtu;
489 }
490 if ((mtu > 0) && (mtu < L2CAPConnection.MINIMUM_MTU) && (name.equals(TRANSMIT_MTU))) {
491 return L2CAPConnection.MINIMUM_MTU;
492 }
493 } catch (NumberFormatException e) {
494 throw new IllegalArgumentException("invalid MTU value " + v);
495 }
496 throw new IllegalArgumentException("invalid MTU param value " + name + "=" + v);
497 }
498
499
500
501
502
503
504
505
506
507
508
509 public static Connection open(String name, int mode) throws IOException {
510 return openImpl(name, mode, false, true);
511 }
512
513
514
515
516
517
518
519
520
521
522
523
524 public static Connection open(String name, int mode, boolean timeouts) throws IOException {
525 return openImpl(name, mode, timeouts, true);
526 }
527
528
529
530
531
532
533
534
535
536
537 public static DataInputStream openDataInputStream(String name) throws IOException {
538 return new DataInputStream(openInputStream(name));
539 }
540
541
542
543
544
545
546
547
548
549
550 public static DataOutputStream openDataOutputStream(String name) throws IOException {
551 return new DataOutputStream(openOutputStream(name));
552 }
553
554
555
556
557
558
559
560
561
562
563 public static InputStream openInputStream(String name) throws IOException {
564 InputConnection con = ((InputConnection) openImpl(name, READ, false, false));
565 try {
566 return con.openInputStream();
567 } finally {
568 con.close();
569 }
570 }
571
572
573
574
575
576
577
578
579
580
581 public static OutputStream openOutputStream(String name) throws IOException {
582 OutputConnection con = ((OutputConnection) openImpl(name, WRITE, false, false));
583 try {
584 return con.openOutputStream();
585 } finally {
586 con.close();
587 }
588 }
589
590 }