001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026: package com.sun.midp.jsr82emul;
027:
028: import java.io.IOException;
029: import java.io.InterruptedIOException;
030: import java.io.OutputStream;
031: import java.io.DataInputStream;
032: import javax.microedition.io.SocketConnection;
033: import javax.microedition.io.Connector;
034: import javax.bluetooth.BluetoothConnectionException;
035: import com.sun.midp.io.BluetoothUrl;
036: import com.sun.midp.jsr082.BluetoothUtils;
037: import com.sun.kvem.jsr082.bluetooth.BCC;
038:
039: /**
040: * Emulates JSR 82 connection.
041: */
042: public class ConnectionEmul extends EmulationClient implements EmulUnit {
043: /** Processes open request in a separate thread. */
044: class Opener extends RunnableProcessor {
045: /** Processes open request. */
046: protected void process() {
047: try {
048: open();
049: } catch (IOException e) {
050: Log.log("Processing CONN_OPEN FAILED ");
051: notifyOpen(handle, false, -1, -1);
052: close();
053: }
054:
055: if (!interrupted) {
056: notifyOpen(handle, true, serviceData.receiveMTU,
057: serviceData.transmitMTU);
058: }
059: opener = null;
060: }
061: }
062:
063: /** Options of service to connect to. */
064: ServiceConnectionData serviceData;
065:
066: /** Opener object needed to perform open operation in a separate thread. */
067: private Opener opener;
068: /** Socket connection that emulates JSR82 one. */
069: private SocketConnection socketConnection;
070: /** Protocol dependent data sener. */
071: private Sender sender;
072: /** Protocol dependent data receiver. */
073: private Receiver receiver;
074:
075: /** Handle that identifies this connection emulation. */
076: public int handle;
077:
078: /**
079: * Opens client-side connection. Requests connection with given client
080: * connection string from emulation server
081: *
082: * @return handle value
083: * @exception IOException if communication to emulation server
084: * or a service fails.
085: */
086: public int open() throws IOException {
087: ServiceConnectionData connData;
088:
089: connect();
090:
091: messenger
092: .sendBytes(
093: toServer,
094: Messenger.CONNECT_TO_SERVICE,
095: serviceData
096: .toByteArray(ServiceConnectionData.CONN_REQUEST_DATA));
097:
098: messenger.receive(fromServer);
099:
100: if (messenger.getCode() != Messenger.SERVICE_AT) {
101: throw new EmulationException(
102: "Unexpected emulation server response "
103: + messenger.getCode());
104: }
105:
106: connData = new ServiceConnectionData(messenger.getBytes(),
107: ServiceConnectionData.CONNECTION_DATA);
108:
109: // no need to communicate with emulation sever any more
110: // since a direct connection is open
111: disconnect();
112:
113: if (connData.error != -1) {
114: throw new BluetoothConnectionException(connData.error);
115: }
116:
117: // vice versa to server side properties
118: serviceData.receiveMTU = connData.transmitMTU;
119: serviceData.transmitMTU = connData.receiveMTU;
120:
121: // applying bitwise operation to get unsigned values from bytes
122: socketConnection = (SocketConnection) new com.sun.midp.io.j2me.socket.Protocol()
123: .openPrim(internalSecurityToken, "//"
124: + (connData.address[0] & 0xff) + "."
125: + (connData.address[1] & 0xff) + "."
126: + (connData.address[2] & 0xff) + "."
127: + (connData.address[3] & 0xff) + ":"
128: + connData.socketPort);
129:
130: connData.address = DeviceEmul.getLocalDeviceEmul().address;
131: sender = Sender.create(socketConnection, serviceData.protocol,
132: handle);
133:
134: // It is important to use blocking sender method here
135: sender.plainSend(connData
136: .toByteArray(ServiceConnectionData.CLIENT_DATA));
137:
138: return handle;
139: }
140:
141: /**
142: * Opens server-side connection, i.e. assigns this emulation with already
143: * open server-side socket connection.
144: * @param socketConnection already open server-side socket connection.
145: * @return handle value
146: * @exception IOException if communication to emulation server
147: * or a service fails.
148: */
149: public int open(SocketConnection socketConnection)
150: throws IOException {
151:
152: this .socketConnection = socketConnection;
153:
154: receiver = Receiver.create(socketConnection,
155: serviceData.protocol, handle);
156:
157: // Calling blocking method intentionally here
158: ServiceConnectionData connData = new ServiceConnectionData(
159: receiver
160: .forceReceive(ServiceConnectionData.CLIENT_DATA_SIZE),
161: ServiceConnectionData.CLIENT_DATA);
162:
163: serviceData.receiveMTU = connData.receiveMTU;
164: serviceData.transmitMTU = connData.transmitMTU;
165: serviceData.address = connData.address;
166:
167: return handle;
168: }
169:
170: /** Close this connection emulation. */
171: public void close() {
172: if (opener != null) {
173: opener.interrupt();
174: opener = null;
175: }
176:
177: if (sender != null) {
178: sender.close();
179: sender = null;
180: }
181:
182: if (receiver != null) {
183: receiver.close();
184: receiver = null;
185: }
186:
187: if (socketConnection != null) {
188: try {
189: socketConnection.close();
190: } catch (IOException e) {
191: // ignoring
192: }
193: socketConnection = null;
194: }
195:
196: ConnRegistry.getInstance().unregister(handle);
197: }
198:
199: /**
200: * Sends data to the other side of the emulated connection.
201: *
202: * @param len amount of bytes to send; given amount of bytes are
203: * sent from output buffer.
204: * @exception IOException if an I/O error occurs
205: */
206: private void send(int len) {
207: byte[] buf = new byte[len];
208:
209: if (getOutData(handle, buf)) {
210: if (sender == null) {
211: sender = Sender.create(socketConnection,
212: serviceData.protocol, handle);
213: }
214: sender.send(buf);
215: }
216:
217: buf = null;
218: }
219:
220: /**
221: * Reads data from emulated connection.
222: */
223: private void receive() {
224: if (receiver == null) {
225: receiver = Receiver.create(socketConnection,
226: serviceData.protocol, handle);
227: }
228:
229: receiver.receive();
230: }
231:
232: /**
233: * Returns actually established ReceiveMTU.
234: * @return established ReceiveMTU.
235: */
236: public int getReceiveMTU() {
237: return serviceData.receiveMTU;
238: }
239:
240: /**
241: * Returns actually established TransmitMTU.
242: * @return established TransmitMTU.
243: */
244: public int getTransmitMTU() {
245: return serviceData.transmitMTU;
246: }
247:
248: /**
249: * Returns Bluetooth address of other side of this connection.
250: * @return Bluetooth address of the other side
251: */
252: public String getRemoteAddress() {
253: return BluetoothUtils.getAddressString(serviceData.address);
254: }
255:
256: /**
257: * Constructs an instance with given handle. Handle is an
258: * integer value that identifies correspondence between connections
259: * in porting layer nd ConnectionEmul instances. In case of client
260: * side connection handle is already defined in native
261: * <code>create_client()</code> functions. In case of server side
262: * it is generated in another constructor.
263: *
264: * @param handle handle receieved form native layer.
265: */
266: public ConnectionEmul(int handle) {
267: this .handle = handle;
268: ConnRegistry.getInstance().register(this .handle, this );
269: }
270:
271: /**
272: * Constructs an instance at server side.
273: * @param serviceData connection options passed by notifier that
274: * creates this connection.
275: */
276: public ConnectionEmul(ServiceConnectionData serviceData) {
277: this (getHandle(serviceData.protocol));
278: this .serviceData = serviceData;
279: }
280:
281: /** Request code for Open operation. */
282: final static int CONN_OPEN = 0;
283: /** Request code for Close operation. */
284: final static int CONN_CLOSE = 1;
285: /** Request code for Init operation. */
286: final static int CONN_INIT = 2;
287: /** Request code for Send operation. */
288: final static int CONN_SEND = 3;
289: /** Request code for Receive operation. */
290: final static int CONN_RECEIVE = 4;
291:
292: /**
293: * Processes request from porting layer to emulation.
294: * @param request packed request to process
295: */
296: public void process(BytePack request) {
297:
298: switch (request.extract()) {
299: case CONN_OPEN:
300: Log.log("Processing CONN_OPEN " + handle);
301: serviceData.address = request
302: .extractBytes(Const.BTADDR_SIZE);
303: serviceData.port = request.extractInt();
304: opener = new Opener();
305: opener.start();
306: break;
307:
308: case CONN_CLOSE:
309: Log.log("Processing CONN_CLOSE " + handle);
310: close();
311: break;
312:
313: case CONN_INIT:
314: Log.log("Processing CONN_INIT " + handle);
315: serviceData = new ServiceConnectionData(
316: request
317: .extractBytes(ServiceConnectionData.SERVER_DATA_SIZE),
318: ServiceConnectionData.SERVER_DATA);
319: break;
320:
321: case CONN_SEND:
322: Log.log("Processing CONN_SEND " + handle);
323: send(request.extractInt());
324: break;
325:
326: case CONN_RECEIVE:
327: Log.log("Processing CONN_RECEIVE " + handle);
328: receive();
329: break;
330:
331: default:
332: Log.log("Processing CONN_UNKNOWN " + handle);
333: throw new EmulationException("Unknown connection request");
334: }
335: }
336:
337: /**
338: * Retrieves new handle for a newly created connection at server side.
339: * @param protocol integer protocol identifier
340: * @return integral handle for a newly created connection
341: */
342: private static native int getHandle(int protocol);
343:
344: /**
345: * Retrieves bytes from output native buffer for sending.
346: * @param handle this connection handle
347: * @param buf buffer to copy data to, it is implied that
348: * exactly <code>buf.length</code> to be sent from
349: * the native buffer
350: * @return <code>true</code> if there is data to be sent,
351: * <code>false<code> otherwise
352: */
353: private static native boolean getOutData(int handle, byte[] buf);
354:
355: /**
356: * Notifies porting layer on receive operation completion.
357: * @param handle connection handle
358: * @param bytes bytes received
359: * @param len amount of bytes received, <code>Const.CONN_FAILURE</code>
360: * if receiving failed, <code>Const.CONN_ENDOF_INP</code> if
361: * there is nothing to receive due reaching end of input stream.
362: */
363: static native void notifyReceived(int handle, byte[] bytes, int len);
364:
365: /**
366: * Notifies porting layer on send operation completion.
367: * @param handle connection handle
368: * @param len amount of bytes sent, <code>CONN_FAILURE</code> if sending
369: * failed
370: */
371: static native void notifySent(int handle, int len);
372:
373: /**
374: * Notifies porting layer on open operation completion.
375: * @param handle this connection handle
376: * @param success <code>true</code> if opened successfully,
377: * <code>false</code> otherwize
378: * @param receiveMTU receiveMTU of L2CAP connection established,
379: * ignored for RFCOMM
380: * @param transmitMTU transmitMTU of L2CAP connection established,
381: * ignored for RFCOMM
382: */
383: private static native void notifyOpen(int handle, boolean success,
384: int receiveMTU, int transmitMTU);
385: }
386:
387: /**
388: * Implements simple send methods that just call
389: * proper stream methods from a separate thread.
390: */
391: class Sender extends RunnableProcessor {
392: /** Socket output stream. */
393: private OutputStream out;
394: /** Data being sent. */
395: protected byte[] outbuf;
396: /** Emulated connection handle. */
397: int handle;
398:
399: /**
400: * Creates proper instance depending on protocol
401: * @param protocol protocol (as in <code>BluetoothUrl</code>) to create
402: * sernder for
403: * @param socketConnection open socet connection to use for sending
404: * @param handle handle of emulated connection to create sender for
405: * @return instance created
406: */
407: static Sender create(SocketConnection socketConnection,
408: int protocol, int handle) {
409:
410: Sender res = null;
411: try {
412: res = protocol == BluetoothUrl.L2CAP ? new L2CAPSender(
413: socketConnection, handle) : new Sender(
414: socketConnection, handle);
415: } catch (IOException e) {
416: ConnectionEmul.notifySent(handle, Const.CONN_FAILURE);
417: }
418:
419: return res;
420: }
421:
422: /**
423: * Initiates streams.
424: * @param socketConnection open socet connection to use.
425: * @param handle handle of emulated connection this sender works for
426: * Exception IOException if I/O error occured while opening streams
427: */
428: Sender(SocketConnection socketConnection, int handle)
429: throws IOException {
430: super (true);
431: this .handle = handle;
432: out = socketConnection.openDataOutputStream();
433: }
434:
435: /** Processes send request. */
436: public void process() {
437: try {
438: sendImpl();
439: } catch (IOException e) {
440: outbuf = null;
441: }
442:
443: if (!interrupted) {
444: int sent = outbuf == null ? Const.CONN_FAILURE
445: : outbuf.length;
446: ConnectionEmul.notifySent(handle, sent);
447: }
448: outbuf = null;
449: }
450:
451: /** Closes assigned streams. */
452: void close() {
453: interrupt();
454: if (out != null) {
455: try {
456: out.close();
457: } catch (IOException e) {
458: // ignoring
459: }
460: out = null;
461: }
462: }
463:
464: /**
465: * Queues givem data for sending.
466: * @param buf bytes to send.
467: */
468: void send(byte[] buf) {
469: if (outbuf != null) {
470: throw new EmulationException("Unexpected sending conflict");
471: }
472: outbuf = buf;
473: start();
474: }
475:
476: /**
477: * Sends data to the other side of the emulated connection.
478: * @exception IOException if an I/O error occurs
479: */
480: protected void sendImpl() throws IOException {
481: plainSend(outbuf);
482: }
483:
484: /**
485: * Sends data by simple OutputStream.write(), ignoring all other
486: * logic impied by Sender and subclasses.
487: * @param buf the data to send
488: */
489: void plainSend(byte[] buf) throws IOException {
490: if (out != null) {
491: out.write(buf, 0, buf.length);
492: out.flush();
493: }
494: }
495: }
496:
497: /**
498: * Reloads send methods in L2CAP-specific way. The L2CAP spacific
499: * is discarding bytes sent in a time by one side and not received by
500: * another.
501: */
502: class L2CAPSender extends Sender {
503: /**
504: * Creates an instance.
505: * @param socketConnection open socet connection to use.
506: * @param handle handle of emulated connection this sender works for
507: * Exception IOException if I/O error occured while opening streams
508: */
509: L2CAPSender(SocketConnection socketConnection, int handle)
510: throws IOException {
511: super (socketConnection, handle);
512: }
513:
514: /**
515: * Sends data to the other side of the emulated connection.
516: * @exception IOException if an I/O error occurs
517: */
518: protected void sendImpl() throws IOException {
519: byte[] lenbytes = new byte[2];
520: lenbytes[0] = (byte) (outbuf.length & 0xff);
521: lenbytes[1] = (byte) ((outbuf.length >> 8) & 0xff);
522:
523: plainSend(lenbytes);
524: plainSend(outbuf);
525: }
526:
527: }
528:
529: /**
530: * Implements simple receive methods that just call
531: * proper I/O streams methods from a separate thread.
532: */
533: class Receiver extends RunnableProcessor {
534: /** Socket input stream. */
535: private DataInputStream in;
536: /** Byes received. */
537: byte[] inbuf;
538: /** Handle of emulated connection this receiver works for. */
539: int handle;
540:
541: /**
542: * Creates proper instance depending on protocol
543: * @param protocol protocol (as in <code>BluetoothUrl</code>) to create
544: * receiver for
545: * @param socketConnection open socket connection to use for receiving
546: * @param handle handle of emulated connection to create receiver for
547: * @return instance created
548: * Exception IOException if I/O error occured while opening streams
549: */
550: static Receiver create(SocketConnection socketConnection,
551: int protocol, int handle) {
552:
553: Receiver res = null;
554: try {
555: res = protocol == BluetoothUrl.L2CAP ? new L2CAPReceiver(
556: socketConnection, handle) : new Receiver(
557: socketConnection, handle);
558: } catch (IOException e) {
559: ConnectionEmul.notifyReceived(handle, null,
560: Const.CONN_FAILURE);
561: }
562:
563: return res;
564: }
565:
566: /**
567: * Initiates streams.
568: * @param socketConnection open socet connection to use.
569: * @param handle handle of emulated connection this receiver works for
570: * Exception IOException if I/O error occured while opening streams
571: */
572: Receiver(SocketConnection socketConnection, int handle)
573: throws IOException {
574: super (true);
575: this .handle = handle;
576: in = socketConnection.openDataInputStream();
577: }
578:
579: /** Processes receive request. */
580: public void process() {
581: int len;
582: try {
583: len = receiveImpl();
584: } catch (Throwable e) {
585: len = Const.CONN_FAILURE;
586: }
587:
588: if (!interrupted) {
589: ConnectionEmul.notifyReceived(handle, inbuf, len);
590: }
591: inbuf = null;
592: }
593:
594: /** Closes assigned stream. */
595: void close() {
596: interrupt();
597: if (in != null) {
598: try {
599: in.close();
600: } catch (IOException e) {
601: // ignoring
602: }
603:
604: in = null;
605: }
606: }
607:
608: /** Initiates receving data. */
609: void receive() {
610: start();
611: }
612:
613: /**
614: * Reads data from emulated connection to given buffer.
615: * @return total number of bytes read into the buffer,
616: * <code>-1</code> if there is nothing to read.
617: *
618: * @exception IOException if an I/O error occurs.
619: */
620: protected int receiveImpl() throws IOException {
621: if (interrupted) {
622: throw new IOException();
623: }
624:
625: int len = in.available();
626: if (len >= 0) {
627: if (len == 0) {
628: len = 1;
629: }
630:
631: inbuf = new byte[len];
632: len = in.read(inbuf, 0, len);
633: }
634:
635: return len;
636: }
637:
638: /**
639: * Reads exactly requested amount of bytes form input stream.
640: * Blocks until they appear.
641: * @param len amount of bytes to read
642: * @return newly created byte array that contains read bytes
643: * @exception IOException if reading fails or connection is closed
644: * while waiting for input
645: */
646: byte[] forceReceive(int len) throws IOException {
647: byte[] bytes = new byte[len];
648: int offset = 0;
649: in.readFully(bytes);
650:
651: return bytes;
652: }
653: }
654:
655: /**
656: * Reloads receive methods in L2CAP-specific way. The L2CAP specific
657: * is discarding bytes sent in a time by one side and not received by
658: * another.
659: */
660: class L2CAPReceiver extends Receiver {
661: /**
662: * Creates an instance.
663: * @param socketConnection open socet connection to use.
664: * @param handle handle of emulated connection this receiver works for
665: * Exception IOException if I/O error occured while opening streams
666: */
667: L2CAPReceiver(SocketConnection socketConnection, int handle)
668: throws IOException {
669: super (socketConnection, handle);
670: }
671:
672: /**
673: * Reads data from emulated connection. It receves exact amount
674: * of bytes that is sent by other side, if requested amount is less
675: * than actually read, last read bytes are discarded.
676: *
677: * @return total number of bytes read into <code>inbuf</code>
678: * @exception IOException if an I/O error occurs
679: */
680: protected int receiveImpl() throws IOException {
681: if (interrupted) {
682: throw new InterruptedIOException();
683: }
684:
685: try {
686: inbuf = forceReceive(2);
687: } catch (IOException e) {
688: // It is OK, just the end of stream reached
689: return Const.CONN_ENDOF_INP;
690: }
691:
692: int packLen = (inbuf[0] & 0xff) | ((inbuf[1] & 0xff) << 8);
693: inbuf = forceReceive(packLen);
694:
695: return packLen;
696: }
697: }
|