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:
027: package com.sun.midp.io.j2me.btl2cap;
028:
029: import java.io.IOException;
030: import java.io.InterruptedIOException;
031: import com.sun.midp.io.BluetoothUrl;
032: import javax.bluetooth.*;
033: import java.util.Enumeration;
034: import com.sun.midp.midlet.MIDletStateHandler;
035: import com.sun.midp.midlet.MIDletSuite;
036: import com.sun.midp.jsr082.bluetooth.BluetoothPush;
037: import com.sun.kvem.jsr082.bluetooth.SDDB;
038: import com.sun.kvem.jsr082.bluetooth.BCC;
039: import com.sun.kvem.jsr082.bluetooth.ServiceRecordImpl;
040: import com.sun.kvem.jsr082.bluetooth.BluetoothNotifier;
041: import com.sun.kvem.jsr082.bluetooth.SDP;
042:
043: /**
044: * Implementation of <code>L2CAPConnectionNotifier</code>.
045: * An instance is created when
046: * <code>Connector.open("btltcap://localhost...") </code>
047: * is called from server application.
048: */
049: public class L2CAPNotifierImpl extends BluetoothNotifier implements
050: L2CAPConnectionNotifier {
051:
052: /** Static initializer. */
053: static {
054: initialize();
055: }
056:
057: /**
058: * Native static class initializer.
059: */
060: private native static void initialize();
061:
062: /**
063: * Native finalizer.
064: * Releases all native resources used by this connection.
065: */
066: private native void finalize();
067:
068: /**
069: * Identidies this connection at native layer,
070: * <code>-1<code> if connection is not open.
071: */
072: private int handle = -1;
073:
074: // IMPL_NOTE make it back private when moving emulation
075: // below porting layer completed
076: /**
077: * Temporary stores peer's native handle for accepted connection.
078: * Used by <code>doAccept</code> method.
079: */
080: int peerHandle = -1;
081:
082: /**
083: * Temporary stores remote device address for accepted connection.
084: */
085: byte[] peerAddress = new byte[6];
086:
087: /** Indicates whether notifier is listening for incoming connections. */
088: private boolean isListenMode = false;
089:
090: /**
091: * Negotiated ReceiveMTU and TransmitMTU.
092: * 16 high bits is ReceiveMTU, 16 low bits is TransmitMTU.
093: *
094: * This packeted value is returned by accept0 method and copied to
095: * L2CAPConnectionImpl by it's constructor.
096: */
097: int mtus = (((-1) << 16) & 0xFFFF0000) & ((-1) & 0xFFFF);
098:
099: /** Keeps PSM value for service record validation. */
100: private DataElement PSM;
101:
102: /** Keeps L2CAP UUID for service record validation. */
103: static final DataElement L2CAP_UUID = new DataElement(
104: DataElement.UUID, new UUID(0x0100));
105:
106: /** Bluetooth PushRegistry handle, used in native methods only. */
107: private int pushHandle = 0;
108:
109: /**
110: * Creates instance of <code>L2CAPNotifierImpl</code>.
111: *
112: * @param url <code>BluetoothUrl</code> that represents server
113: * connection string to create notifier for
114: * @param mode I/O access mode
115: * @throws IOException if an I/O error occurs
116: */
117: L2CAPNotifierImpl(BluetoothUrl url, int mode) throws IOException {
118: super (url, mode);
119:
120: MIDletSuite suite = MIDletStateHandler.getMidletStateHandler()
121: .getMIDletSuite();
122:
123: int suiteId = (suite != null) ? suite.getID()
124: : MIDletSuite.UNUSED_SUITE_ID;
125: String connUrl = "btl2cap:" + url.caseSensitiveUrl;
126:
127: // check whether suiteId is valid.
128: // for example, it can be null if L2CAPNotifier is created as SDP server
129: // in PushRegistry inbound connection watcher thread that can be started
130: // before MIDlet start.
131: if (suiteId != MIDletSuite.UNUSED_SUITE_ID
132: && pushCheckout(connUrl, suiteId)) {
133: serviceRec = BluetoothPush.getServiceRecord(this , connUrl);
134: checkServiceRecord();
135: } else {
136: serviceRec = createServiceRecord(this , url, doCreate(url));
137: }
138: }
139:
140: /**
141: * Checks out (takes ownership of) an active server connection maintained
142: * by push subsystem.
143: *
144: * @param url URL used during registration of the push entry
145: * @param suiteId suite id
146: * @return true if the operation succeeds, false otherwise
147: */
148: private native boolean pushCheckout(String url, int suiteId);
149:
150: /**
151: * Creates an empty service record for the given URL and PSM value.
152: *
153: * @param notifier L2CAP notifier object to be associated with the record
154: * @param url URL from which a new record is constructed
155: * @param psm PSM value assigned to the notifier
156: * @return a new service record instance
157: */
158: public static ServiceRecordImpl createServiceRecord(
159: L2CAPNotifierImpl notifier, BluetoothUrl url, int psm) {
160: DataElement serviceList = new DataElement(DataElement.DATSEQ);
161: serviceList.addElement(new DataElement(DataElement.UUID,
162: new UUID(url.uuid, false)));
163: DataElement protocolList = new DataElement(DataElement.DATSEQ);
164: DataElement protocol = new DataElement(DataElement.DATSEQ);
165: protocol.addElement(L2CAP_UUID);
166: if (notifier != null) {
167: notifier.PSM = new DataElement(DataElement.U_INT_2, psm);
168: protocol.addElement(notifier.PSM);
169: } else {
170: protocol.addElement(new DataElement(DataElement.U_INT_2,
171: psm));
172: }
173: protocolList.addElement(protocol);
174: int[] attrIDs;
175: DataElement[] attrVals;
176: if (url.name != null) {
177: DataElement name = new DataElement(DataElement.STRING,
178: url.name);
179: attrIDs = new int[] {
180: ServiceRecordImpl.SERVICE_CLASS_ATTR_ID,
181: ServiceRecordImpl.PROTOCOL_DESCRIPTOR_LIST,
182: ServiceRecordImpl.NAME_ATTR_ID };
183: attrVals = new DataElement[] { serviceList, protocolList,
184: name };
185: } else {
186: attrIDs = new int[] {
187: ServiceRecordImpl.SERVICE_CLASS_ATTR_ID,
188: ServiceRecordImpl.PROTOCOL_DESCRIPTOR_LIST };
189: attrVals = new DataElement[] { serviceList, protocolList };
190: }
191: return new ServiceRecordImpl(notifier, attrIDs, attrVals);
192: }
193:
194: /**
195: * Creates an empty service record for the given URL.
196: *
197: * @param url URL from which a new record is constructed
198: * @return a new service record instance
199: */
200: public static ServiceRecordImpl createServiceRecord(String url) {
201: return createServiceRecord(null, new BluetoothUrl(url), 0);
202: }
203:
204: /**
205: * Ensures that the service record is valid.
206: *
207: * @throws ServiceRegistrationException if the structure of the
208: * <code>srvRecord</code> is missing any mandatory service
209: * attributes, or if an attempt has been made to change any of the
210: * values described as fixed
211: */
212: protected void checkServiceRecord()
213: throws ServiceRegistrationException {
214: synchronized (serviceRec) {
215: // check if the ServiceClassIDList is not missing
216: if (serviceRec
217: .getAttributeValue(ServiceRecordImpl.SERVICE_CLASS_ATTR_ID) == null) {
218: throw new ServiceRegistrationException(
219: "ServiceClassIDList is missing.");
220: }
221: // check the ProtocolList is not missed
222: DataElement protocolList = serviceRec
223: .getAttributeValue(ServiceRecordImpl.PROTOCOL_DESCRIPTOR_LIST);
224: if (protocolList == null) {
225: throw new ServiceRegistrationException(
226: "ProtocolDescriptorList is missing.");
227: }
228: Enumeration protocolListEnum = (Enumeration) protocolList
229: .getValue();
230: while (protocolListEnum.hasMoreElements()) {
231: Enumeration protocolEnum = (Enumeration) ((DataElement) protocolListEnum
232: .nextElement()).getValue();
233: // check that the L2CAP UUID is not missing, check the PSM
234: // is not missing and the value has not changed
235: if (protocolEnum.hasMoreElements()) {
236: if (compareDataElements((DataElement) protocolEnum
237: .nextElement(), L2CAP_UUID)) {
238: if (!protocolEnum.hasMoreElements()) {
239: throw new ServiceRegistrationException(
240: "PSM value is missing.");
241: }
242: if (PSM == null) {
243: PSM = new DataElement(DataElement.U_INT_2,
244: ((DataElement) protocolEnum
245: .nextElement()).getLong());
246: return;
247: }
248: if (!compareDataElements(
249: (DataElement) protocolEnum
250: .nextElement(), PSM)) {
251: throw new ServiceRegistrationException(
252: "PSM value has changed.");
253: }
254: return;
255: }
256: }
257: }
258: }
259: throw new ServiceRegistrationException("L2CAP UUID is missing.");
260: }
261:
262: /**
263: * Ensures that this notifier can accept connections.
264: *
265: * @throws IOException if notifier is closed, device is not
266: * in connectable mode, or service record is invalid
267: */
268: private void ensureConnectable() throws IOException {
269: if (isClosed) {
270: throw new BluetoothConnectionException(
271: BluetoothConnectionException.FAILED_NOINFO,
272: "Notifier is closed.");
273: }
274: if (!BCC.getInstance().isConnectable()) {
275: throw new BluetoothStateException(
276: "The device is not connectable.");
277: }
278: checkServiceRecord();
279: }
280:
281: /**
282: * Accepts connections blocking until incoming client connection appears.
283: *
284: * @return connection to a client just accepted.
285: *
286: * @throws IOException if notifier is closed or device is not
287: * in connectable mode.
288: */
289: public L2CAPConnection acceptAndOpen() throws IOException {
290: ensureConnectable();
291:
292: // switch on listen mode if it has not been done yet
293: doListen();
294:
295: // adds the service record if it is not yet in SDDB
296: SDDB.getInstance().updateServiceRecord(serviceRec);
297:
298: L2CAPConnection client;
299: do {
300: ensureConnectable();
301:
302: // accept incoming connections if any
303: client = doAccept();
304: } while (client == null);
305:
306: return client;
307: }
308:
309: /**
310: * Closes this notifier making corresponding service record inaccessible.
311: * updates the information on the communication server.
312: *
313: * @exception IOException if an error occured lower in Bluetooth stack.
314: */
315: public void close() throws IOException {
316: if (isClosed) {
317: return;
318: }
319: isClosed = true;
320:
321: SDDB.getInstance().removeServiceRecord(serviceRec);
322:
323: doClose();
324: }
325:
326: /**
327: * Force listen mode by calling underlying stack methods.
328: *
329: * @throws IOException if an error occured
330: */
331: private void doListen() throws IOException {
332: // force listening if it had not been done yet
333: if (!isListenMode) {
334: listen0();
335:
336: isListenMode = true;
337: }
338: }
339:
340: /**
341: * Force Bluetooth stack to listen for incoming client connections.
342: *
343: * Note: the method gets native connection handle directly from
344: * <code>handle<code> field of <code>L2CAPNotifierImpl</code> object.
345: *
346: * @throws IOException if an I/O error occurs
347: */
348: private native void listen0() throws IOException;
349:
350: /**
351: * Advertises acception by calling underlying stack methods.
352: *
353: * @return L2CAPConnectionImpl instance to work with accepted client
354: */
355: private L2CAPConnection doAccept() throws IOException {
356: if (!isListenMode) {
357: throw new BluetoothStateException(
358: "Device is not in listen mode");
359: }
360:
361: /*
362: * Note: native handle is set to peerHandleID field directly by accept0
363: * method and retrieved by L2CAPConnectionImpl constructor.
364: */
365: mtus = accept0();
366:
367: return new L2CAPConnectionImpl(url, mode, this );
368: }
369:
370: /**
371: * Accepts incoming client connection request.
372: *
373: * Note: the method gets native connection handle directly from
374: * <code>handle<code> field of <code>L2CAPNotifierImpl</code> object.
375: *
376: * Note: new native connection handle to work with accepted incoming
377: * client connection is setted directly to <code>handle</code> field of
378: * appropriate <code>L2CAPConnectionImpl</code> object.
379: *
380: * @return Negotiated ReceiveMTU and TransmitMTU.
381: * 16 high bits is ReceiveMTU, 16 low bits is TransmitMTU.
382: * @throws IOException if an I/O error occurs
383: */
384: private native int accept0() throws IOException;
385:
386: /**
387: * Closes this notifier at native layer.
388: */
389: private void doClose() throws IOException {
390: close0();
391: }
392:
393: /**
394: * Closes this server connection.
395: * Releases all native resources (such as sockets) owned by this notifier.
396: *
397: * Note: the method gets native connection handle directly from
398: * <code>handle<code> field of <code>L2CAPNotifierImpl</code> object.
399: *
400: * @throws IOException IOException if an I/O error occurs
401: */
402: private native void close0() throws IOException;
403:
404: /**
405: * Creates an instanse of server connection at native layer.
406: *
407: * @param url <code>BluetoothUrl</code> that represents server
408: * connection string to create notifier for
409: * @return selected psm value to listen for incoming connections on
410: */
411: private int doCreate(BluetoothUrl url) throws IOException {
412: return create0(url.receiveMTU, url.transmitMTU,
413: url.authenticate, url.authorize, url.encrypt,
414: url.master);
415: }
416:
417: /**
418: * Creates a server connection.
419: *
420: * Note: the method sets native connection handle directly to
421: * <code>handle<code> field of <code>L2CAPNotifierImpl</code> object.
422: *
423: * @param imtu receive MTU or <code>-1</code> if not specified
424: * @param omtu transmit MTU or <code>-1</code> if not specified
425: * @param auth <code>true</code> if authication is required
426: * @param authz <code>true</code> if authorization is required
427: * @param enc <code>true</code> indicates
428: * what connection must be encrypted
429: * @param master <code>true</code> if client requires to be
430: * a connection's master
431: * @return selected psm value to listen for incoming connections on
432: * @throws IOException IOException if an I/O error occurs
433: */
434: private native int create0(int imtu, int omtu, boolean auth,
435: boolean authz, boolean enc, boolean master)
436: throws IOException;
437:
438: }
|