FileDocCategorySizeDatePackage
L2CAPConnectionImpl.javaAPI DocphoneME MR2 API (J2ME)13322Wed May 02 18:00:30 BST 2007com.sun.midp.io.j2me.btl2cap

L2CAPConnectionImpl.java

/*
 *
 *
 * Copyright  1990-2007 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt).
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions.
 */
package com.sun.midp.io.j2me.btl2cap;

import javax.bluetooth.L2CAPConnection;
import javax.bluetooth.BluetoothConnectionException;
import com.sun.kvem.jsr082.bluetooth.BluetoothConnection;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.Vector;
import com.sun.midp.io.BluetoothUrl;
import com.sun.midp.jsr082.BluetoothUtils;

/**
 * Provides the <code>javax.bluetooth.L2CAPConnection</code>
 * connection implemetation.
 */
public class L2CAPConnectionImpl extends BluetoothConnection
        implements L2CAPConnection {
    /** Static initializer. */
    static {
        initialize();
    }

    /**
     * Native static class initializer.
     */
    private native static void initialize();

    /**
     * Native finalizer.
     * Releases all native resources used by this connection.
     */
    private native void finalize();

    /**
     * Stores the address of the remote device connected by this connection.
     * The value is set by the constructor.
     */
    byte[] remoteDeviceAddress;

    /** Lock object for reading from the socket */
    private final Object readerLock = new Object();

    /** Lock object for writing to the socket */
    private final Object writerLock = new Object();

    /**
     * Negotiated ReceiveMTU and TransmitMTU.
     * 16 high bits is ReceiveMTU, 16 low bits is TransmitMTU.
     *
     * This packeted value is returned by L2CAPConnectionImpl.connect0 and
     * L2CAPNotifierImpl.accept0 methods and  decoded by doReceiveMTU
     * and doTransmitMTU methods.
     */
    int mtus = (((-1) << 16) & 0xFFFF0000) & ((-1) & 0xFFFF);
    
    /**
     * Identifies this connection at native layer,
     * <code>-1<code> if connection is not open.
     *
     * Note: in real mode this field is accessed only from native code.
     */
    private int handle = -1;

    /** The receive MTU for the connection. */
    private int receiveMTU  = -1;

    /** The transmit MTU for the connection. */
    private int transmitMTU = -1;

    /**
     * Constructs an instance and opens connection.
     *
     * @param url keeps connection details
     * @param mode I/O access mode
     * @exception IOException if connection fails
     */
    protected L2CAPConnectionImpl(BluetoothUrl url, int mode)
            throws IOException {
        this(url, mode, null);
    }

    /**
     * Constructs an instance and
     * sets up corresponding native connection handle to it.
     *
     * @param url keeps connection details
     * @param mode I/O access mode
     * @param notif corresponding <code>L2CAPNotifierImpl</code> instance
     *              temporary storing native peer handle
     * @exception IOException if connection fails
     */
    protected L2CAPConnectionImpl(BluetoothUrl url,
            int mode, L2CAPNotifierImpl notif) throws IOException {
        super(url, mode);

        if (notif == null) {
            remoteDeviceAddress = BluetoothUtils.getAddressBytes(url.address);
            doOpen();
        } else {
            remoteDeviceAddress = new byte[6];
            System.arraycopy(notif.peerAddress, 0,  remoteDeviceAddress, 0, 6);

            setThisConnHandle0(notif);
            // copy negotiated MTUs returned by L2CAPNotifierImpl.accept0
            mtus = notif.mtus;
        }

        receiveMTU = (mtus >> 16) & 0xFFFF;
        transmitMTU = mtus & 0xFFFF;
        
        // Check whether transmit MTU was increased during connection
        // establishment phase. If it was, set original MTU value.
        // IMPL_NOTE: pass updated transmit MTU to underlaying Bluetooth stack.
        if (url.transmitMTU != -1 &&
                transmitMTU > url.transmitMTU) {
            transmitMTU = url.transmitMTU;
        }

        setRemoteDevice();
    }

    /**
     * Retrieves native connection handle from temporary storage
     * inside <code>L2CAPNotifierImpl</code> instance
     * and sets it to this <code>L2CAPConnectionImpl</code> instance.
     *
     * Note: the method sets native connection handle directly to
     * <code>handle<code> field of <code>L2CAPConnectionImpl</code> object.
     *
     * @param notif reference to corresponding <code>L2CAPNotifierImpl</code>
     *              instance storing native peer handle
     */
    private native void setThisConnHandle0(L2CAPNotifierImpl notif);
    
    /**
     * Retrieves address of remote device on the other side of this connection.
     *
     * @return remote device address
     */
    public String getRemoteDeviceAddress() {
        return BluetoothUtils.getAddressString(remoteDeviceAddress);
    }
    
    /**
     * Returns ReceiveMTU.
     *
     * @return receive MTU
     *
     * @throws IOException if the connection is closed.
     */
    public final int getReceiveMTU() throws IOException {
        if (isClosed()) {
            throw new IOException("Connection is closed");
        }

        return receiveMTU;
    }

    /**
     * Returns TransmitMTU.
     *
     * @return transmit MTU
     *
     * @throws IOException if the connection is closed.
     */
    public final int getTransmitMTU() throws IOException {
        if (isClosed()) {
            throw new IOException("Connection is closed");
        }

        return transmitMTU;
    }

    /**
     * Sends given bytes to this connection.
     *
     * Note: the method is non-blocking.
     *
     * @param data bytes to send.
     *
     * @throws IOException if either connection is closed or I/O error occured
     */
    public void send(byte[] data) throws IOException {
        checkOpen();
        checkWriteMode();
        if (data == null) {
            throw new NullPointerException("The data is null");
        }
            
        int len = (data.length < transmitMTU) ? data.length : transmitMTU;
        int sentBytes;
        
        /*
         * Multiple threads blocked on write operation may return results
         * interleaved arbitrarily. From an application perspective, the
         * results would be indeterministic. So "writer locks" are
         * introduced for "write" operation to the same socket.
         */
        synchronized (writerLock) {
            sentBytes = send0(data, 0, len);
        }

        if (sentBytes != len) {
            throw new IOException("Data sending failed");
        }
    }

    /**
     * Receives data from this connection.
     *
     * Note: The method is blocking.
     *
     * @param buf byte array to place data received to
     * @return amount of bytes received
     * @throws IOException if either connection is closed or I/O error occured
     */
    public int receive(byte[] buf) throws IOException {
        checkOpen();
        checkReadMode();
        if (buf == null) {
            throw new NullPointerException("The buffer is null");
        }
        if (buf.length == 0) {
            return 0;
        }
        int len = (buf.length > receiveMTU) ? receiveMTU : buf.length;
        
        /*
         * Multiple threads blocked on read operation may
         * return results interleaved arbitrarily. From an
         * application perspective, the results would be
         * indeterministic. So "reader locks" are introduced
         * for "read" operation from the same handle.
         */
        synchronized (readerLock) {
            return receive0(buf, 0, len);
        }
    }

    /**
     * Checks if there is data to receive without blocking.
     * @return true if any data can be retrieved via
     *        <code>receive()</code> method without blocking.
     * @throws IOException if the connection is closed.
     */
    public boolean ready() throws IOException {
        checkOpen();
        return ready0();
    }

    /**
     * Closes this connection.
     * @throws IOException if I/O error.
     */
    public void close() throws IOException {
        synchronized (this) {
            if (isClosed()) {
                return;
            }
            resetRemoteDevice();
        }
        close0();
    }

    /**
     * Receives data from this connection.
     *
     * Note: the method gets native connection handle directly from
     * <code>handle<code> field of <code>L2CAPConnectionImpl</code> object.
     *
     * @param buf the buffer to read to
     * @param off start offset in <code>buf</code> array
     *               at which the data to be written
     * @param size the maximum number of bytes to read,
     *             the rest of the packet is discarded.
     * @return total number of bytes read into the buffer or
     *             <code>0</code> if a zero length packet is received
     * @throws IOException if an I/O error occurs
     */
    private native int receive0(byte[] buf, int off, int size)
            throws IOException;
    
    /**
     * Sends the specified data to this connection.
     *
     * Note: the method gets native connection handle directly from
     * <code>handle<code> field of <code>L2CAPConnectionImpl</code> object.
     *
     * @param buf the data to send
     * @param off the offset into the data buffer
     * @param len the length of the data in the buffer
     * @return total number of send bytes,
     *         or <code>-1</code> if nothing is send
     * @throws IOException if an I/O error occurs
     */
    private native int send0(byte[] buf, int off, int len) throws IOException;
    
    /**
     * Checks if there is data to receive without blocking.
     *
     * Note: the method gets native connection handle directly from
     * <code>handle<code> field of <code>L2CAPConnectionImpl</code> object.
     *
     * @return <code>true</code> if a packet is present,
     *         <code>false</code> otherwise
     * @throws IOException if any I/O error occurs
     */
    private native boolean ready0() throws IOException;
    
    /**
     * Closes client connection.
     *
     * Note: the method gets native connection handle directly from
     * <code>handle<code> field of <code>L2CAPConnectionImpl</code> object.
     *
     * @throws IOException if any I/O error occurs
     */
    private native void close0() throws IOException;
    

    /** Opens client connection */
    private void doOpen() throws IOException {
        // create native connection object
        // Note: the method sets resulting native connection handle
        // directly to field <code>handle<code>.
        create0(url.receiveMTU, url.transmitMTU, url.authenticate,
            url.encrypt, url.master);

        byte[] address = BluetoothUtils.getAddressBytes(url.address);
        
        try {
            // establish connection
            mtus = connect0(address, url.port);
        } catch (IOException e) {
            throw new BluetoothConnectionException(
                BluetoothConnectionException.FAILED_NOINFO,
                e.getMessage());
        }
    }

    /**
     * Creates a client connection object.
     *
     * Note: the method gets native connection handle directly from
     * <code>handle<code> field of <code>L2CAPConnectionImpl</code> object.
     *
     * @param imtu receive MTU or <code>-1</code> if not specified
     * @param omtu transmit MTU or <code>-1</code> if not specified
     * @param auth   <code>true</code> if authication is required
     * @param enc    <code>true</code> indicates
     *                what connection must be encrypted
     * @param master <code>true</code> if client requires to be
     *               a connection's master
     * @throws IOException if any I/O error occurs
     */
    private native void create0(int imtu, int omtu, boolean auth,
        boolean enc, boolean master) throws IOException;

    /**
     * Starts client connection establishment.
     *
     * Note: the method gets native connection handle directly from
     * <code>handle<code> field of <code>L2CAPConnectionImpl</code> object.
     *
     * @param addr bluetooth address of device to connect to
     * @param psm Protocol Service Multiplexor (PSM) value
     * @return Negotiated ReceiveMTU and TransmitMTU.
     *               16 high bits is ReceiveMTU, 16 low bits is TransmitMTU.
     * @throws IOException if any I/O error occurs
     */
    private native int connect0(byte[] addr, int psm) throws IOException;
    
}