/*
*
*
* Copyright 1990-2009 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.jsr082.bluetooth.btl2cap;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.Vector;
import javax.bluetooth.L2CAPConnection;
import javax.bluetooth.BluetoothConnectionException;
import com.sun.jsr082.bluetooth.BluetoothConnection;
import com.sun.jsr082.bluetooth.BluetoothUrl;
import com.sun.jsr082.bluetooth.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.
*/
protected 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 = sendData(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 receiveData(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
*/
protected int receiveData(byte[] buf, int off, int size)
throws IOException {
return receive0(buf, off, size);
}
/*
* 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
*/
protected 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
*/
protected int sendData(byte[] buf, int off, int len) throws IOException {
return send0(buf, off, len);
}
/*
* 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
*/
protected 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;
}