summaryrefslogtreecommitdiff
path: root/java/src
diff options
context:
space:
mode:
authorMark Spruiell <mes@zeroc.com>2017-03-30 15:58:21 -0700
committerMark Spruiell <mes@zeroc.com>2017-03-30 15:58:21 -0700
commit975309430b7edf9aed1c5829931804f188b5ccbb (patch)
tree72ea7b998c17b651d035f87ba256ee3a18258f8e /java/src
parentUpdate bzip2 package version used in CSharp nuget package (diff)
downloadice-975309430b7edf9aed1c5829931804f188b5ccbb.tar.bz2
ice-975309430b7edf9aed1c5829931804f188b5ccbb.tar.xz
ice-975309430b7edf9aed1c5829931804f188b5ccbb.zip
adding IceBT for Java8
Diffstat (limited to 'java/src')
-rw-r--r--java/src/IceBT/build.gradle38
-rw-r--r--java/src/IceBT/src/main/java/android/bluetooth/BluetoothAdapter.java55
-rw-r--r--java/src/IceBT/src/main/java/android/bluetooth/BluetoothDevice.java25
-rw-r--r--java/src/IceBT/src/main/java/android/bluetooth/BluetoothServerSocket.java19
-rw-r--r--java/src/IceBT/src/main/java/android/bluetooth/BluetoothSocket.java40
-rw-r--r--java/src/IceBT/src/main/java/com/zeroc/IceBT/AcceptorI.java230
-rw-r--r--java/src/IceBT/src/main/java/com/zeroc/IceBT/ConnectorI.java114
-rw-r--r--java/src/IceBT/src/main/java/com/zeroc/IceBT/EndpointFactoryI.java62
-rw-r--r--java/src/IceBT/src/main/java/com/zeroc/IceBT/EndpointI.java562
-rw-r--r--java/src/IceBT/src/main/java/com/zeroc/IceBT/Instance.java57
-rw-r--r--java/src/IceBT/src/main/java/com/zeroc/IceBT/PluginFactory.java33
-rw-r--r--java/src/IceBT/src/main/java/com/zeroc/IceBT/PluginI.java46
-rw-r--r--java/src/IceBT/src/main/java/com/zeroc/IceBT/TransceiverI.java700
13 files changed, 1981 insertions, 0 deletions
diff --git a/java/src/IceBT/build.gradle b/java/src/IceBT/build.gradle
new file mode 100644
index 00000000000..017a80c2ab1
--- /dev/null
+++ b/java/src/IceBT/build.gradle
@@ -0,0 +1,38 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2017 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+//sourceCompatibility = iceSourceCompatibility
+//targetCompatibility = iceTargetCompatibility
+sourceCompatibility = 1.8
+targetCompatibility = 1.8
+
+project.ext.displayName = "IceBT"
+project.ext.description = "Bluetooth support for Ice"
+
+slice {
+ java {
+ set1 {
+ files = fileTree(dir: "$sliceDir/IceBT", includes:['*.ice'], excludes:["*F.ice"])
+ }
+ }
+}
+
+dependencies {
+ compile project(':ice')
+}
+
+apply from: "$rootProject.projectDir/gradle/library.gradle"
+
+jar {
+ //
+ // The classes in src/main/java/android/bluetooth are stubs that allow us to compile the IceBT transport
+ // plug-in without requiring an Android SDK. These classes are excluded from the IceBT JAR file.
+ //
+ exclude("android/**")
+}
diff --git a/java/src/IceBT/src/main/java/android/bluetooth/BluetoothAdapter.java b/java/src/IceBT/src/main/java/android/bluetooth/BluetoothAdapter.java
new file mode 100644
index 00000000000..8d00d879d3b
--- /dev/null
+++ b/java/src/IceBT/src/main/java/android/bluetooth/BluetoothAdapter.java
@@ -0,0 +1,55 @@
+//
+// This is a placeholder for the Android API. It is not included in the IceBT JAR file.
+//
+
+package android.bluetooth;
+
+public final class BluetoothAdapter
+{
+ public boolean cancelDiscovery()
+ {
+ return false;
+ }
+
+ public boolean isEnabled()
+ {
+ return false;
+ }
+
+ public String getAddress()
+ {
+ return "";
+ }
+
+ public static boolean checkBluetoothAddress(String address)
+ {
+ return false;
+ }
+
+ public static BluetoothAdapter getDefaultAdapter()
+ {
+ return null;
+ }
+
+ public BluetoothDevice getRemoteDevice(byte[] address)
+ {
+ return null;
+ }
+
+ public BluetoothDevice getRemoteDevice(String address)
+ {
+ return null;
+ }
+
+ public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, java.util.UUID uuid)
+ throws java.io.IOException
+ {
+ return null;
+ }
+
+ public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, java.util.UUID uuid)
+ throws java.io.IOException
+ {
+ return null;
+ }
+}
diff --git a/java/src/IceBT/src/main/java/android/bluetooth/BluetoothDevice.java b/java/src/IceBT/src/main/java/android/bluetooth/BluetoothDevice.java
new file mode 100644
index 00000000000..7fbee8880de
--- /dev/null
+++ b/java/src/IceBT/src/main/java/android/bluetooth/BluetoothDevice.java
@@ -0,0 +1,25 @@
+//
+// This is a placeholder for the Android API. It is not included in the IceBT JAR file.
+//
+
+package android.bluetooth;
+
+public final class BluetoothDevice
+{
+ public BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID uuid)
+ throws java.io.IOException
+ {
+ return null;
+ }
+
+ public BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID uuid)
+ throws java.io.IOException
+ {
+ return null;
+ }
+
+ public String getAddress()
+ {
+ return "";
+ }
+}
diff --git a/java/src/IceBT/src/main/java/android/bluetooth/BluetoothServerSocket.java b/java/src/IceBT/src/main/java/android/bluetooth/BluetoothServerSocket.java
new file mode 100644
index 00000000000..c6e6f2a6978
--- /dev/null
+++ b/java/src/IceBT/src/main/java/android/bluetooth/BluetoothServerSocket.java
@@ -0,0 +1,19 @@
+//
+// This is a placeholder for the Android API. It is not included in the IceBT JAR file.
+//
+
+package android.bluetooth;
+
+public final class BluetoothServerSocket implements java.io.Closeable
+{
+ public BluetoothSocket accept()
+ throws java.io.IOException
+ {
+ return null;
+ }
+
+ public void close()
+ throws java.io.IOException
+ {
+ }
+}
diff --git a/java/src/IceBT/src/main/java/android/bluetooth/BluetoothSocket.java b/java/src/IceBT/src/main/java/android/bluetooth/BluetoothSocket.java
new file mode 100644
index 00000000000..80470a11a52
--- /dev/null
+++ b/java/src/IceBT/src/main/java/android/bluetooth/BluetoothSocket.java
@@ -0,0 +1,40 @@
+//
+// This is a placeholder for the Android API. It is not included in the IceBT JAR file.
+//
+
+package android.bluetooth;
+
+public final class BluetoothSocket implements java.io.Closeable
+{
+ public void close()
+ throws java.io.IOException
+ {
+ }
+
+ public void connect()
+ throws java.io.IOException
+ {
+ }
+
+ public java.io.InputStream getInputStream()
+ throws java.io.IOException
+ {
+ return null;
+ }
+
+ public java.io.OutputStream getOutputStream()
+ throws java.io.IOException
+ {
+ return null;
+ }
+
+ public BluetoothDevice getRemoteDevice()
+ {
+ return null;
+ }
+
+ public boolean isConnected()
+ {
+ return false;
+ }
+}
diff --git a/java/src/IceBT/src/main/java/com/zeroc/IceBT/AcceptorI.java b/java/src/IceBT/src/main/java/com/zeroc/IceBT/AcceptorI.java
new file mode 100644
index 00000000000..d320816f4ed
--- /dev/null
+++ b/java/src/IceBT/src/main/java/com/zeroc/IceBT/AcceptorI.java
@@ -0,0 +1,230 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2017 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+package com.zeroc.IceBT;
+
+import com.zeroc.IceInternal.Acceptor;
+import com.zeroc.IceInternal.ReadyCallback;
+import com.zeroc.IceInternal.SocketOperation;
+import com.zeroc.IceInternal.Transceiver;
+import com.zeroc.Ice.SocketException;
+
+import android.bluetooth.BluetoothSocket;
+import android.bluetooth.BluetoothServerSocket;
+import java.util.UUID;
+
+final class AcceptorI implements Acceptor
+{
+ @Override
+ public java.nio.channels.ServerSocketChannel fd()
+ {
+ return null;
+ }
+
+ @Override
+ public void setReadyCallback(ReadyCallback callback)
+ {
+ _readyCallback = callback;
+ }
+
+ @Override
+ public void close()
+ {
+ synchronized(this)
+ {
+ _closed = true;
+ }
+
+ if(_socket != null)
+ {
+ try
+ {
+ _socket.close(); // Wakes up the thread blocked in accept().
+ }
+ catch(Exception ex)
+ {
+ // Ignore.
+ }
+ }
+ if(_thread != null)
+ {
+ try
+ {
+ _thread.join();
+ }
+ catch(Exception ex)
+ {
+ // Ignore.
+ }
+ }
+ }
+
+ @Override
+ public com.zeroc.IceInternal.EndpointI listen()
+ {
+ try
+ {
+ //
+ // We always listen using the "secure" method.
+ //
+ _socket = _instance.bluetoothAdapter().listenUsingRfcommWithServiceRecord(_name, _uuid);
+ }
+ catch(java.io.IOException ex)
+ {
+ throw new SocketException(ex);
+ }
+
+ //
+ // Use a helper thread to perform the blocking accept() calls.
+ //
+ _thread = new Thread()
+ {
+ public void run()
+ {
+ runAccept();
+ }
+ };
+ _thread.start();
+
+ return _endpoint;
+ }
+
+ @Override
+ public synchronized Transceiver accept()
+ {
+ if(_exception != null)
+ {
+ throw new SocketException(_exception);
+ }
+
+ //
+ // accept() should only be called when we have at least one socket ready.
+ //
+ assert(!_pending.isEmpty());
+
+ BluetoothSocket socket = _pending.pop();
+
+ //
+ // Update our status with the thread pool.
+ //
+ _readyCallback.ready(SocketOperation.Read, !_pending.isEmpty());
+
+ return new TransceiverI(_instance, socket, _uuid, _adapterName);
+ }
+
+ @Override
+ public String protocol()
+ {
+ return _instance.protocol();
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuffer s = new StringBuffer("local address = ");
+ s.append(_instance.bluetoothAdapter().getAddress());
+ return s.toString();
+ }
+
+ @Override
+ public String toDetailedString()
+ {
+ StringBuffer s = new StringBuffer(toString());
+ if(!_name.isEmpty())
+ {
+ s.append("\nservice name = '");
+ s.append(_name);
+ s.append("'");
+ }
+ if(_uuid != null)
+ {
+ s.append("\nservice uuid = ");
+ s.append(_uuid.toString());
+ }
+ return s.toString();
+ }
+
+ AcceptorI(EndpointI endpoint, Instance instance, String adapterName, UUID uuid, String name)
+ {
+ _endpoint = endpoint;
+ _instance = instance;
+ _adapterName = adapterName;
+ _name = name;
+ _uuid = uuid;
+
+ _pending = new java.util.Stack<BluetoothSocket>();
+ _closed = false;
+ }
+
+ private void runAccept()
+ {
+ try
+ {
+ while(true)
+ {
+ BluetoothSocket socket = _socket.accept();
+ synchronized(this)
+ {
+ _pending.push(socket);
+
+ //
+ // Notify the thread pool that we are ready to "read". The thread pool will invoke accept()
+ // and we can return a new transceiver.
+ //
+ _readyCallback.ready(SocketOperation.Read, true);
+ }
+ }
+ }
+ catch(Exception ex)
+ {
+ synchronized(this)
+ {
+ if(!_closed)
+ {
+ _exception = ex;
+ _readyCallback.ready(SocketOperation.Read, true);
+ }
+ }
+ }
+
+ //
+ // Close any remaining incoming sockets that haven't been accepted yet.
+ //
+ java.util.Stack<BluetoothSocket> pending;
+ synchronized(this)
+ {
+ pending = _pending;
+ _pending = null;
+ }
+
+ for(BluetoothSocket s : pending)
+ {
+ try
+ {
+ s.close();
+ }
+ catch(Exception ex)
+ {
+ // Ignore.
+ }
+ }
+ }
+
+ private EndpointI _endpoint;
+ private Instance _instance;
+ private String _adapterName;
+ private String _name;
+ private UUID _uuid;
+ private ReadyCallback _readyCallback;
+ private BluetoothServerSocket _socket;
+ private java.util.Stack<BluetoothSocket> _pending;
+ private Thread _thread;
+ private Exception _exception;
+ private boolean _closed;
+}
diff --git a/java/src/IceBT/src/main/java/com/zeroc/IceBT/ConnectorI.java b/java/src/IceBT/src/main/java/com/zeroc/IceBT/ConnectorI.java
new file mode 100644
index 00000000000..8a3cef88d06
--- /dev/null
+++ b/java/src/IceBT/src/main/java/com/zeroc/IceBT/ConnectorI.java
@@ -0,0 +1,114 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2017 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+package com.zeroc.IceBT;
+
+import com.zeroc.IceInternal.Connector;
+import com.zeroc.IceInternal.HashUtil;
+import com.zeroc.IceInternal.Transceiver;
+import com.zeroc.Ice.SocketException;
+
+import java.util.UUID;
+
+final class ConnectorI implements Connector
+{
+ @Override
+ public Transceiver connect()
+ {
+ return new TransceiverI(_instance, _addr, _uuid, _connectionId);
+ }
+
+ @Override
+ public short type()
+ {
+ return _instance.type();
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ if(!_addr.isEmpty())
+ {
+ buf.append(_addr);
+ }
+ if(_uuid != null)
+ {
+ if(!_addr.isEmpty())
+ {
+ buf.append(';');
+ }
+ buf.append(_uuid.toString());
+ }
+ return buf.toString();
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return _hashCode;
+ }
+
+ //
+ // Only for use by EndpointI.
+ //
+ ConnectorI(Instance instance, String addr, UUID uuid, int timeout, String connectionId)
+ {
+ _instance = instance;
+ _addr = addr;
+ _uuid = uuid;
+ _timeout = timeout;
+ _connectionId = connectionId;
+
+ _hashCode = 5381;
+ _hashCode = HashUtil.hashAdd(_hashCode , _addr);
+ _hashCode = HashUtil.hashAdd(_hashCode , _uuid);
+ _hashCode = HashUtil.hashAdd(_hashCode , _timeout);
+ _hashCode = HashUtil.hashAdd(_hashCode , _connectionId);
+ }
+
+ @Override
+ public boolean equals(java.lang.Object obj)
+ {
+ if(!(obj instanceof ConnectorI))
+ {
+ return false;
+ }
+
+ if(this == obj)
+ {
+ return true;
+ }
+
+ ConnectorI p = (ConnectorI)obj;
+ if(!_uuid.equals(p._uuid))
+ {
+ return false;
+ }
+
+ if(_timeout != p._timeout)
+ {
+ return false;
+ }
+
+ if(!_connectionId.equals(p._connectionId))
+ {
+ return false;
+ }
+
+ return _addr.equals(p._addr);
+ }
+
+ private Instance _instance;
+ private String _addr;
+ private UUID _uuid;
+ private int _timeout;
+ private String _connectionId;
+ private int _hashCode;
+}
diff --git a/java/src/IceBT/src/main/java/com/zeroc/IceBT/EndpointFactoryI.java b/java/src/IceBT/src/main/java/com/zeroc/IceBT/EndpointFactoryI.java
new file mode 100644
index 00000000000..66c2b391438
--- /dev/null
+++ b/java/src/IceBT/src/main/java/com/zeroc/IceBT/EndpointFactoryI.java
@@ -0,0 +1,62 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2017 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+package com.zeroc.IceBT;
+
+import com.zeroc.IceInternal.EndpointFactory;
+import com.zeroc.IceInternal.ProtocolInstance;
+
+final class EndpointFactoryI implements EndpointFactory
+{
+ EndpointFactoryI(Instance instance)
+ {
+ _instance = instance;
+ }
+
+ @Override
+ public short type()
+ {
+ return _instance.type();
+ }
+
+ @Override
+ public String protocol()
+ {
+ return _instance.protocol();
+ }
+
+ @Override
+ public com.zeroc.IceInternal.EndpointI create(java.util.ArrayList<String> args, boolean oaEndpoint)
+ {
+ EndpointI endpt = new EndpointI(_instance);
+ endpt.initWithOptions(args, oaEndpoint);
+ return endpt;
+ }
+
+ @Override
+ public com.zeroc.IceInternal.EndpointI read(com.zeroc.Ice.InputStream s)
+ {
+ return new EndpointI(_instance, s);
+ }
+
+ @Override
+ public void destroy()
+ {
+ _instance.destroy();
+ _instance = null;
+ }
+
+ @Override
+ public EndpointFactory clone(ProtocolInstance inst, EndpointFactory del)
+ {
+ return new EndpointFactoryI(new Instance(_instance.communicator(), inst.type(), inst.protocol()));
+ }
+
+ private Instance _instance;
+}
diff --git a/java/src/IceBT/src/main/java/com/zeroc/IceBT/EndpointI.java b/java/src/IceBT/src/main/java/com/zeroc/IceBT/EndpointI.java
new file mode 100644
index 00000000000..b57cc25368b
--- /dev/null
+++ b/java/src/IceBT/src/main/java/com/zeroc/IceBT/EndpointI.java
@@ -0,0 +1,562 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2017 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+package com.zeroc.IceBT;
+
+import com.zeroc.IceInternal.Acceptor;
+import com.zeroc.IceInternal.Connector;
+import com.zeroc.IceInternal.EndpointI_connectors;
+import com.zeroc.IceInternal.HashUtil;
+import com.zeroc.IceInternal.Transceiver;
+import com.zeroc.Ice.EndpointParseException;
+import com.zeroc.Ice.EndpointSelectionType;
+import com.zeroc.Ice.InputStream;
+import com.zeroc.Ice.MarshalException;
+import com.zeroc.Ice.OutputStream;
+
+import android.bluetooth.BluetoothAdapter;
+import java.util.UUID;
+
+final class EndpointI extends com.zeroc.IceInternal.EndpointI
+{
+ public EndpointI(Instance instance, String addr, UUID uuid, String name, int channel, int timeout,
+ String connectionId, boolean compress)
+ {
+ _instance = instance;
+ _addr = addr;
+ _uuid = uuid;
+ _name = name;
+ _channel = channel;
+ _timeout = timeout;
+ _connectionId = connectionId;
+ _compress = compress;
+ hashInit();
+ }
+
+ public EndpointI(Instance instance)
+ {
+ _instance = instance;
+ _addr = "";
+ _uuid = null;
+ _name = "";
+ _channel = 0;
+ _timeout = instance.defaultTimeout();
+ _connectionId = "";
+ _compress = false;
+ }
+
+ public EndpointI(Instance instance, InputStream s)
+ {
+ _instance = instance;
+
+ //
+ // _name and _channel are not marshaled.
+ //
+ _name = "";
+ _channel = 0;
+
+ _addr = s.readString().toUpperCase();
+ if(!BluetoothAdapter.checkBluetoothAddress(_addr))
+ {
+ throw new MarshalException("invalid address `" + _addr + "' in endpoint");
+ }
+
+ try
+ {
+ _uuid = UUID.fromString(s.readString());
+ }
+ catch(IllegalArgumentException ex)
+ {
+ throw new MarshalException("invalid UUID for Bluetooth endpoint", ex);
+ }
+
+ _timeout = s.readInt();
+ _compress = s.readBool();
+ hashInit();
+ }
+
+ @Override
+ public void streamWriteImpl(OutputStream s)
+ {
+ //
+ // _name and _channel are not marshaled.
+ //
+ s.writeString(_addr);
+ s.writeString(_uuid.toString());
+ s.writeInt(_timeout);
+ s.writeBool(_compress);
+ }
+
+ @Override
+ public short type()
+ {
+ return _instance.type();
+ }
+
+ @Override
+ public String protocol()
+ {
+ return _instance.protocol();
+ }
+
+ @Override
+ public int timeout()
+ {
+ return _timeout;
+ }
+
+ @Override
+ public com.zeroc.IceInternal.EndpointI timeout(int timeout)
+ {
+ if(timeout == _timeout)
+ {
+ return this;
+ }
+ else
+ {
+ return new EndpointI(_instance, _addr, _uuid, _name, _channel, timeout, _connectionId, _compress);
+ }
+ }
+
+ @Override
+ public String connectionId()
+ {
+ return _connectionId;
+ }
+
+ @Override
+ public com.zeroc.IceInternal.EndpointI connectionId(String connectionId)
+ {
+ if(connectionId.equals(_connectionId))
+ {
+ return this;
+ }
+ else
+ {
+ return new EndpointI(_instance, _addr, _uuid, _name, _channel, _timeout, connectionId, _compress);
+ }
+ }
+
+ @Override
+ public boolean compress()
+ {
+ return _compress;
+ }
+
+ @Override
+ public com.zeroc.IceInternal.EndpointI compress(boolean compress)
+ {
+ if(compress == _compress)
+ {
+ return this;
+ }
+ else
+ {
+ return new EndpointI(_instance, _addr, _uuid, _name, _channel, _timeout, _connectionId, compress);
+ }
+ }
+
+ @Override
+ public boolean datagram()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean secure()
+ {
+ return _instance.secure();
+ }
+
+ @Override
+ public Transceiver transceiver()
+ {
+ return null;
+ }
+
+ @Override
+ public void connectors_async(EndpointSelectionType selType, EndpointI_connectors callback)
+ {
+ java.util.List<Connector> conns = new java.util.ArrayList<Connector>();
+ conns.add(new ConnectorI(_instance, _addr, _uuid, _timeout, _connectionId));
+ callback.connectors(conns);
+ }
+
+ @Override
+ public Acceptor acceptor(String adapterName)
+ {
+ return new AcceptorI(this, _instance, adapterName, _uuid, _name);
+ }
+
+ @Override
+ public java.util.List<com.zeroc.IceInternal.EndpointI> expand()
+ {
+ java.util.List<com.zeroc.IceInternal.EndpointI> endps =
+ new java.util.ArrayList<com.zeroc.IceInternal.EndpointI>();
+ endps.add(this);
+ return endps;
+ }
+
+ @Override
+ public boolean equivalent(com.zeroc.IceInternal.EndpointI endpoint)
+ {
+ if(!(endpoint instanceof EndpointI))
+ {
+ return false;
+ }
+ EndpointI btEndpointI = (EndpointI)endpoint;
+ return btEndpointI.type() == type() && btEndpointI._addr.equals(_addr) && btEndpointI._uuid.equals(_uuid) &&
+ btEndpointI._channel == _channel;
+ }
+
+ @Override
+ public String options()
+ {
+ //
+ // WARNING: Certain features, such as proxy validation in Glacier2,
+ // depend on the format of proxy strings. Changes to toString() and
+ // methods called to generate parts of the reference string could break
+ // these features. Please review for all features that depend on the
+ // format of proxyToString() before changing this and related code.
+ //
+ String s = "";
+
+ if(_addr != null && _addr.length() > 0)
+ {
+ s += " -a ";
+ boolean addQuote = _addr.indexOf(':') != -1;
+ if(addQuote)
+ {
+ s += "\"";
+ }
+ s += _addr;
+ if(addQuote)
+ {
+ s += "\"";
+ }
+ }
+
+ if(_uuid != null)
+ {
+ s += " -u ";
+ String uuidStr = _uuid.toString();
+ boolean addQuote = uuidStr.indexOf(':') != -1;
+ if(addQuote)
+ {
+ s += "\"";
+ }
+ s += uuidStr;
+ if(addQuote)
+ {
+ s += "\"";
+ }
+ }
+
+ if(_channel > 0)
+ {
+ s += " -c " + _channel;
+ }
+
+ if(_timeout == -1)
+ {
+ s += " -t infinite";
+ }
+ else
+ {
+ s += " -t " + _timeout;
+ }
+
+ if(_compress)
+ {
+ s += " -z";
+ }
+
+ return s;
+ }
+
+ public void initWithOptions(java.util.ArrayList<String> args, boolean oaEndpoint)
+ {
+ super.initWithOptions(args);
+
+ if(_addr.length() == 0)
+ {
+ _addr = _instance.defaultHost();
+ if(_addr == null)
+ {
+ _addr = "";
+ }
+ }
+
+ if(_addr.length() == 0 || _addr.equals("*"))
+ {
+ if(oaEndpoint)
+ {
+ //
+ // Ignore a missing address, we always use the default adapter anyway.
+ //
+ }
+ else
+ {
+ throw new EndpointParseException(
+ "a device address must be specified using the -a option or Ice.Default.Host");
+ }
+ }
+
+ if(_name.length() == 0)
+ {
+ _name = "Ice Service";
+ }
+
+ if(_uuid == null)
+ {
+ if(oaEndpoint)
+ {
+ //
+ // Generate a UUID for object adapters that don't specify one.
+ //
+ _uuid = UUID.randomUUID();
+ }
+ else
+ {
+ throw new EndpointParseException("a UUID must be specified using the -u option");
+ }
+ }
+
+ hashInit();
+ }
+
+ @Override
+ public com.zeroc.Ice.EndpointInfo getInfo()
+ {
+ EndpointInfo info = new EndpointInfo()
+ {
+ @Override
+ public short type()
+ {
+ return EndpointI.this.type();
+ }
+
+ @Override
+ public boolean datagram()
+ {
+ return EndpointI.this.datagram();
+ }
+
+ @Override
+ public boolean secure()
+ {
+ return EndpointI.this.secure();
+ }
+ };
+ info.addr = _addr;
+ info.uuid = _uuid.toString();
+ return info;
+ }
+
+ @Override
+ public int compareTo(com.zeroc.IceInternal.EndpointI obj) // From java.lang.Comparable
+ {
+ if(!(obj instanceof EndpointI))
+ {
+ return type() < obj.type() ? -1 : 1;
+ }
+
+ EndpointI p = (EndpointI)obj;
+ if(this == p)
+ {
+ return 0;
+ }
+
+ int v = _addr.compareTo(p._addr);
+ if(v != 0)
+ {
+ return v;
+ }
+
+ v = _uuid.toString().compareTo(p._uuid.toString());
+ if(v != 0)
+ {
+ return v;
+ }
+
+ if(_channel < p._channel)
+ {
+ return -1;
+ }
+ else if(p._channel < _channel)
+ {
+ return 1;
+ }
+
+ if(_timeout < p._timeout)
+ {
+ return -1;
+ }
+ else if(p._timeout < _timeout)
+ {
+ return 1;
+ }
+
+ v = _connectionId.compareTo(p._connectionId);
+ if(v != 0)
+ {
+ return v;
+ }
+
+ if(!_compress && p._compress)
+ {
+ return -1;
+ }
+ else if(!p._compress && _compress)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return _hashValue;
+ }
+
+ @Override
+ protected boolean checkOption(String option, String argument, String endpoint)
+ {
+ if(super.checkOption(option, argument, endpoint))
+ {
+ return true;
+ }
+
+ if(option.equals("-a"))
+ {
+ if(argument == null)
+ {
+ throw new EndpointParseException("no argument provided for -a option in endpoint " + endpoint);
+ }
+ if(!argument.equals("*") && !BluetoothAdapter.checkBluetoothAddress(argument.toUpperCase()))
+ {
+ throw new EndpointParseException("invalid address provided for -a option in endpoint " + endpoint);
+ }
+ _addr = argument.toUpperCase(); // Android requires a hardware address to use upper case letters.
+ }
+ else if(option.equals("-u"))
+ {
+ if(argument == null)
+ {
+ throw new EndpointParseException("no argument provided for -u option in endpoint " + endpoint);
+ }
+ try
+ {
+ _uuid = UUID.fromString(argument);
+ }
+ catch(IllegalArgumentException ex)
+ {
+ throw new EndpointParseException("invalid UUID for Bluetooth endpoint", ex);
+ }
+ }
+ else if(option.equals("-c"))
+ {
+ if(argument == null)
+ {
+ throw new EndpointParseException("no argument provided for -c option in endpoint " + endpoint);
+ }
+
+ try
+ {
+ _channel = Integer.parseInt(argument);
+ }
+ catch(NumberFormatException ex)
+ {
+ throw new EndpointParseException("invalid channel value `" + argument + "' in endpoint " + endpoint);
+ }
+
+ if(_channel < 0 || _channel > 30) // RFCOMM channel limit is 30
+ {
+ throw new EndpointParseException("channel value `" + argument + "' out of range in endpoint " +
+ endpoint);
+ }
+ }
+ else if(option.equals("-t"))
+ {
+ if(argument == null)
+ {
+ throw new EndpointParseException("no argument provided for -t option in endpoint " + endpoint);
+ }
+
+ if(argument.equals("infinite"))
+ {
+ _timeout = -1;
+ }
+ else
+ {
+ try
+ {
+ _timeout = Integer.parseInt(argument);
+ if(_timeout < 1)
+ {
+ throw new EndpointParseException("invalid timeout value `" + argument + "' in endpoint " +
+ endpoint);
+ }
+ }
+ catch(NumberFormatException ex)
+ {
+ throw new EndpointParseException("invalid timeout value `" + argument + "' in endpoint " +
+ endpoint);
+ }
+ }
+ }
+ else if(option.equals("-z"))
+ {
+ if(argument != null)
+ {
+ throw new EndpointParseException("unexpected argument `" + argument +
+ "' provided for -z option in " + endpoint);
+ }
+
+ _compress = true;
+ }
+ else if(option.equals("--name"))
+ {
+ if(argument == null)
+ {
+ throw new EndpointParseException("no argument provided for --name option in endpoint " + endpoint);
+ }
+
+ _name = argument;
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+ }
+
+ private void hashInit()
+ {
+ int h = 5381;
+ h = HashUtil.hashAdd(h, _addr);
+ h = HashUtil.hashAdd(h, _uuid.toString());
+ h = HashUtil.hashAdd(h, _timeout);
+ h = HashUtil.hashAdd(h, _connectionId);
+ h = HashUtil.hashAdd(h, _compress);
+ _hashValue = h;
+ }
+
+ private Instance _instance;
+ private String _addr;
+ private UUID _uuid;
+ private String _name;
+ private int _channel;
+ private int _timeout;
+ private String _connectionId;
+ private boolean _compress;
+ private int _hashValue;
+}
diff --git a/java/src/IceBT/src/main/java/com/zeroc/IceBT/Instance.java b/java/src/IceBT/src/main/java/com/zeroc/IceBT/Instance.java
new file mode 100644
index 00000000000..326424062de
--- /dev/null
+++ b/java/src/IceBT/src/main/java/com/zeroc/IceBT/Instance.java
@@ -0,0 +1,57 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2017 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+package com.zeroc.IceBT;
+
+import com.zeroc.Ice.Communicator;
+import com.zeroc.Ice.PluginInitializationException;
+
+import android.bluetooth.BluetoothAdapter;
+
+class Instance extends com.zeroc.IceInternal.ProtocolInstance
+{
+ Instance(Communicator communicator, short type, String protocol)
+ {
+ //
+ // We consider the transport to be "secure" because it uses the secure versions Android's Bluetooth API
+ // methods for establishing and accepting connections. The boolean argument below sets secure=true.
+ //
+ super(communicator, type, protocol, true);
+
+ _communicator = communicator;
+
+ _bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ if(_bluetoothAdapter == null)
+ {
+ throw new PluginInitializationException("bluetooth adapter not available");
+ }
+ else if(!_bluetoothAdapter.isEnabled())
+ {
+ throw new PluginInitializationException("bluetooth is not enabled");
+ }
+ }
+
+ void destroy()
+ {
+ _communicator = null;
+ }
+
+ Communicator communicator()
+ {
+ return _communicator;
+ }
+
+ BluetoothAdapter bluetoothAdapter()
+ {
+ return _bluetoothAdapter;
+ }
+
+ private Communicator _communicator;
+ private BluetoothAdapter _bluetoothAdapter;
+}
diff --git a/java/src/IceBT/src/main/java/com/zeroc/IceBT/PluginFactory.java b/java/src/IceBT/src/main/java/com/zeroc/IceBT/PluginFactory.java
new file mode 100644
index 00000000000..0143e792a5c
--- /dev/null
+++ b/java/src/IceBT/src/main/java/com/zeroc/IceBT/PluginFactory.java
@@ -0,0 +1,33 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2017 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+package com.zeroc.IceBT;
+
+/**
+ * Plug-in factories must implement this interface.
+ **/
+public class PluginFactory implements com.zeroc.Ice.PluginFactory
+{
+ /**
+ * Returns a new plug-in.
+ *
+ * @param communicator The communicator for the plug-in.
+ * @param name The name of the plug-in.
+ * @param args The arguments that are specified in the plug-in's configuration.
+ *
+ * @return The new plug-in. <code>null</code> can be returned to indicate
+ * that a general error occurred. Alternatively, <code>create</code> can throw
+ * PluginInitializationException to provide more detailed information.
+ **/
+ @Override
+ public com.zeroc.Ice.Plugin create(com.zeroc.Ice.Communicator communicator, String name, String[] args)
+ {
+ return new PluginI(communicator);
+ }
+}
diff --git a/java/src/IceBT/src/main/java/com/zeroc/IceBT/PluginI.java b/java/src/IceBT/src/main/java/com/zeroc/IceBT/PluginI.java
new file mode 100644
index 00000000000..2da1c7920a7
--- /dev/null
+++ b/java/src/IceBT/src/main/java/com/zeroc/IceBT/PluginI.java
@@ -0,0 +1,46 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2017 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+package com.zeroc.IceBT;
+
+class PluginI implements com.zeroc.Ice.Plugin
+{
+ public PluginI(com.zeroc.Ice.Communicator communicator)
+ {
+ final com.zeroc.IceInternal.ProtocolPluginFacade facade =
+ com.zeroc.IceInternal.Util.getProtocolPluginFacade(communicator);
+
+ //
+ // Register the endpoint factory. We have to do this now, rather than
+ // in initialize, because the communicator may need to interpret
+ // proxies before the plug-in is fully initialized.
+ //
+ EndpointFactoryI factory =
+ new EndpointFactoryI(new Instance(communicator, com.zeroc.Ice.BTEndpointType.value, "bt"));
+ facade.addEndpointFactory(factory);
+
+ com.zeroc.IceInternal.EndpointFactory sslFactory =
+ facade.getEndpointFactory(com.zeroc.Ice.SSLEndpointType.value);
+ if(sslFactory != null)
+ {
+ Instance instance = new Instance(communicator, com.zeroc.Ice.BTSEndpointType.value, "bts");
+ facade.addEndpointFactory(sslFactory.clone(instance, new EndpointFactoryI(instance)));
+ }
+ }
+
+ @Override
+ public void initialize()
+ {
+ }
+
+ @Override
+ public void destroy()
+ {
+ }
+}
diff --git a/java/src/IceBT/src/main/java/com/zeroc/IceBT/TransceiverI.java b/java/src/IceBT/src/main/java/com/zeroc/IceBT/TransceiverI.java
new file mode 100644
index 00000000000..94f56119695
--- /dev/null
+++ b/java/src/IceBT/src/main/java/com/zeroc/IceBT/TransceiverI.java
@@ -0,0 +1,700 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2017 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+package com.zeroc.IceBT;
+
+import com.zeroc.IceInternal.Buffer;
+import com.zeroc.IceInternal.ReadyCallback;
+import com.zeroc.IceInternal.SocketOperation;
+import com.zeroc.IceInternal.Transceiver;
+import com.zeroc.Ice.LocalException;
+import com.zeroc.Ice.SocketException;
+
+import java.lang.Thread;
+import java.nio.ByteBuffer;
+import java.nio.channels.*;
+import java.util.UUID;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSocket;
+
+final class TransceiverI implements Transceiver
+{
+ @Override
+ public SelectableChannel fd()
+ {
+ //
+ // Android doesn't provide non-blocking APIs for Bluetooth.
+ //
+ return null;
+ }
+
+ @Override
+ public void setReadyCallback(ReadyCallback callback)
+ {
+ _readyCallback = callback;
+ }
+
+ @Override
+ public synchronized int initialize(Buffer readBuffer, Buffer writeBuffer)
+ {
+ if(_exception != null)
+ {
+ throw _exception;
+ //throw (LocalException) _exception.fillInStackTrace();
+ }
+
+ if(_state == StateConnecting)
+ {
+ //
+ // Wait until the connect thread is finished.
+ //
+ return SocketOperation.Read;
+ }
+ else if(_state == StateConnected)
+ {
+ //
+ // Update our Read state to indicate whether we still have more data waiting to be read.
+ //
+ _readyCallback.ready(SocketOperation.Read, _readBuffer.b.position() > 0);
+ }
+
+ return SocketOperation.None;
+ }
+
+ @Override
+ public int closing(boolean initiator, LocalException ex)
+ {
+ //
+ // If we are initiating the connection closure, wait for the peer
+ // to close the connection. Otherwise, close immediately.
+ //
+ return initiator ? SocketOperation.Read : SocketOperation.None;
+ }
+
+ @Override
+ public void close()
+ {
+ Thread connectThread = null, readThread = null, writeThread = null;
+
+ synchronized(this)
+ {
+ //
+ // Close the socket first in order to interrupt the helper threads.
+ //
+ if(_socket != null)
+ {
+ try
+ {
+ _socket.close();
+ }
+ catch(java.io.IOException ex)
+ {
+ // Ignore.
+ }
+ _socket = null;
+ }
+
+ connectThread = _connectThread;
+ _connectThread = null;
+ readThread = _readThread;
+ _readThread = null;
+ writeThread = _writeThread;
+ _writeThread = null;
+
+ _state = StateClosed;
+
+ if(writeThread != null)
+ {
+ notifyAll(); // Wake up the read/write threads.
+ }
+ }
+
+ if(connectThread != null)
+ {
+ try
+ {
+ connectThread.join();
+ }
+ catch(InterruptedException ex)
+ {
+ // Ignore.
+ }
+ }
+
+ if(readThread != null)
+ {
+ try
+ {
+ readThread.join();
+ }
+ catch(InterruptedException ex)
+ {
+ // Ignore.
+ }
+ }
+
+ if(writeThread != null)
+ {
+ try
+ {
+ writeThread.join();
+ }
+ catch(InterruptedException ex)
+ {
+ // Ignore.
+ }
+ }
+ }
+
+ @Override
+ public com.zeroc.IceInternal.EndpointI bind()
+ {
+ assert(false);
+ return null;
+ }
+
+ @Override
+ public synchronized int write(Buffer buf)
+ {
+ if(_exception != null)
+ {
+ throw _exception;
+ //throw (LocalException) _exception.fillInStackTrace();
+ }
+
+ //
+ // Accept up to _sndSize bytes in our internal buffer.
+ //
+ final int capacity = _sndSize - _writeBuffer.b.position();
+ if(capacity > 0)
+ {
+ final int num = Math.min(capacity, buf.b.remaining());
+ _writeBuffer.expand(num);
+ final int lim = buf.b.limit(); // Save the current limit.
+ buf.b.limit(buf.b.position() + num); // Temporarily change the limit.
+ _writeBuffer.b.put(buf.b); // Copy to our internal buffer.
+ buf.b.limit(lim); // Restore the previous limit.
+
+ notifyAll(); // We've added data to the internal buffer, so wake up the write thread.
+ }
+
+ return buf.b.hasRemaining() ? SocketOperation.Write : SocketOperation.None;
+ }
+
+ @Override
+ public synchronized int read(Buffer buf)
+ {
+ if(_exception != null)
+ {
+ throw _exception;
+ //throw (LocalException) _exception.fillInStackTrace();
+ }
+
+ //
+ // Copy the requested amount of data from our internal buffer to the given buffer.
+ //
+ _readBuffer.b.flip();
+ if(_readBuffer.b.hasRemaining())
+ {
+ int bytesAvailable = _readBuffer.b.remaining();
+ int bytesNeeded = buf.b.remaining();
+ if(bytesAvailable > bytesNeeded)
+ {
+ bytesAvailable = bytesNeeded;
+ }
+ if(buf.b.hasArray())
+ {
+ //
+ // Copy directly into the destination buffer's backing array.
+ //
+ byte[] arr = buf.b.array();
+ _readBuffer.b.get(arr, buf.b.arrayOffset() + buf.b.position(), bytesAvailable);
+ buf.b.position(buf.b.position() + bytesAvailable);
+ }
+ else if(_readBuffer.b.hasArray())
+ {
+ //
+ // Copy directly from the source buffer's backing array.
+ //
+ byte[] arr = _readBuffer.b.array();
+ buf.b.put(arr, _readBuffer.b.arrayOffset() + _readBuffer.b.position(), bytesAvailable);
+ _readBuffer.b.position(_readBuffer.b.position() + bytesAvailable);
+ }
+ else
+ {
+ //
+ // Copy using a temporary array.
+ //
+ byte[] arr = new byte[bytesAvailable];
+ _readBuffer.b.get(arr);
+ buf.b.put(arr);
+ }
+ }
+ _readBuffer.b.compact();
+
+ //
+ // The read thread will temporarily stop reading if we exceed our configured limit.
+ //
+ if(_readBuffer.b.position() < _rcvSize)
+ {
+ notifyAll();
+ }
+
+ //
+ // Update our Read state to indicate whether we still have more data waiting to be read.
+ //
+ _readyCallback.ready(SocketOperation.Read, _readBuffer.b.position() > 0);
+
+ return buf.b.hasRemaining() ? SocketOperation.Read : SocketOperation.None;
+ }
+
+ @Override
+ public String protocol()
+ {
+ return _instance.protocol();
+ }
+
+ @Override
+ public String toString()
+ {
+ return _desc;
+ }
+
+ @Override
+ public String toDetailedString()
+ {
+ return toString();
+ }
+
+ @Override
+ public com.zeroc.Ice.ConnectionInfo getInfo()
+ {
+ ConnectionInfo info = new ConnectionInfo();
+ info.incoming = _adapterName != null;
+ info.adapterName = _adapterName != null ? _adapterName : "";
+ info.connectionId = _connectionId;
+ info.rcvSize = _rcvSize;
+ info.sndSize = _sndSize;
+ info.localAddress = BluetoothAdapter.getDefaultAdapter().getAddress();
+ //info.localChannel - not available, use default value of -1
+ info.remoteAddress = _remoteAddr;
+ //info.remoteChannel - not available, use default value of -1
+ info.uuid = _uuid.toString();
+ return info;
+ }
+
+ @Override
+ public synchronized void setBufferSize(int rcvSize, int sndSize)
+ {
+ _rcvSize = Math.max(1024, rcvSize);
+ _sndSize = Math.max(1024, sndSize);
+ }
+
+ @Override
+ public void checkSendSize(Buffer buf)
+ {
+ }
+
+ //
+ // Used by ConnectorI.
+ //
+ TransceiverI(Instance instance, String remoteAddr, UUID uuid, String connectionId)
+ {
+ _instance = instance;
+ _remoteAddr = remoteAddr;
+ _uuid = uuid;
+ _connectionId = connectionId;
+ _state = StateConnecting;
+
+ init();
+
+ BluetoothAdapter adapter = _instance.bluetoothAdapter();
+ assert(adapter != null);
+
+ BluetoothDevice device = null;
+ try
+ {
+ device = adapter.getRemoteDevice(_remoteAddr);
+ }
+ catch(IllegalArgumentException ex)
+ {
+ //
+ // Illegal address - This should have been detected by the endpoint.
+ //
+ assert(false);
+ throw new SocketException(ex);
+ }
+
+ try
+ {
+ //
+ // We always connect using the "secure" method.
+ //
+ _socket = device.createRfcommSocketToServiceRecord(_uuid);
+ }
+ catch(java.io.IOException ex)
+ {
+ throw new SocketException(ex);
+ }
+
+ _connectThread = new Thread()
+ {
+ public void run()
+ {
+ String name = "IceBT.ConnectThread";
+ if(_remoteAddr != null && !_remoteAddr.isEmpty())
+ {
+ name += "-" + _remoteAddr;
+ }
+ if(_uuid != null)
+ {
+ name += "-" + _uuid.toString();
+ }
+ setName(name);
+
+ runConnectThread();
+ }
+ };
+ _connectThread.start();
+ }
+
+ //
+ // Used by AcceptorI.
+ //
+ TransceiverI(Instance instance, BluetoothSocket socket, UUID uuid, String adapterName)
+ {
+ _instance = instance;
+ _remoteAddr = socket.getRemoteDevice().getAddress();
+ _uuid = uuid;
+ _connectionId = "";
+ _adapterName = adapterName;
+ _socket = socket;
+ _state = StateConnected;
+
+ init();
+
+ startReadWriteThreads();
+ }
+
+ private void init()
+ {
+ _desc = "local address = " + _instance.bluetoothAdapter().getAddress();
+ if(_remoteAddr != null && !_remoteAddr.isEmpty())
+ {
+ _desc += "\nremote address = " + _remoteAddr;
+ }
+ if(_uuid != null)
+ {
+ _desc += "\nservice uuid = " + _uuid.toString();
+ }
+
+ final int defaultBufSize = 128 * 1024;
+ _rcvSize = _instance.properties().getPropertyAsIntWithDefault("IceBT.RcvSize", defaultBufSize);
+ _sndSize = _instance.properties().getPropertyAsIntWithDefault("IceBT.SndSize", defaultBufSize);
+
+ _readBuffer = new Buffer(false);
+ _writeBuffer = new Buffer(false);
+ }
+
+ private synchronized void exception(LocalException ex)
+ {
+ if(_exception == null)
+ {
+ _exception = ex;
+ }
+ }
+
+ private void runConnectThread()
+ {
+ //
+ // Always cancel discovery prior to a connect attempt.
+ //
+ _instance.bluetoothAdapter().cancelDiscovery();
+
+ try
+ {
+ //
+ // This can block for several seconds.
+ //
+ _socket.connect();
+ }
+ catch(java.io.IOException ex)
+ {
+ exception(new com.zeroc.Ice.ConnectFailedException(ex));
+ }
+
+ synchronized(this)
+ {
+ _connectThread = null;
+
+ if(_exception == null)
+ {
+ //
+ // Connect succeeded.
+ //
+ _state = StateConnected;
+
+ startReadWriteThreads();
+ }
+ }
+
+ //
+ // This causes the Ice run time to invoke initialize() again.
+ //
+ _readyCallback.ready(SocketOperation.Read, true);
+ }
+
+ private void startReadWriteThreads()
+ {
+ String s = "";
+ if(_remoteAddr != null && !_remoteAddr.isEmpty())
+ {
+ s += "-" + _remoteAddr;
+ }
+ if(_uuid != null)
+ {
+ s += "-" + _uuid.toString();
+ }
+ final String suffix = s;
+
+ _readThread = new Thread()
+ {
+ public void run()
+ {
+ setName("IceBT.ReadThread" + suffix);
+
+ runReadThread();
+ }
+ };
+ _readThread.start();
+
+ _writeThread = new Thread()
+ {
+ public void run()
+ {
+ setName("IceBT.WriteThread" + suffix);
+
+ runWriteThread();
+ }
+ };
+ _writeThread.start();
+ }
+
+ private void runReadThread()
+ {
+ java.io.InputStream in = null;
+
+ try
+ {
+ byte[] buf = null;
+
+ synchronized(this)
+ {
+ if(_socket == null)
+ {
+ return;
+ }
+ in = _socket.getInputStream();
+
+ buf = new byte[_rcvSize];
+ }
+
+ while(true)
+ {
+ ByteBuffer b = null;
+
+ synchronized(this)
+ {
+ //
+ // If we've read too much data, wait until the application consumes some before we read again.
+ //
+ while(_state == StateConnected && _exception == null && _readBuffer.b.position() > _rcvSize)
+ {
+ try
+ {
+ wait();
+ }
+ catch(InterruptedException ex)
+ {
+ break;
+ }
+ }
+
+ if(_state != StateConnected || _exception != null)
+ {
+ break;
+ }
+ }
+
+ int num = in.read(buf);
+ if(num > 0)
+ {
+ synchronized(this)
+ {
+ _readBuffer.expand(num);
+ _readBuffer.b.put(buf, 0, num);
+ _readyCallback.ready(SocketOperation.Read, true);
+
+ if(buf.length != _rcvSize)
+ {
+ //
+ // Application must have called setBufferSize.
+ //
+ buf = new byte[_rcvSize];
+ }
+ }
+ }
+ }
+ }
+ catch(java.io.IOException ex)
+ {
+ exception(new SocketException(ex));
+ //
+ // Mark as ready for reading so that the Ice run time will invoke read() and we can report the exception.
+ //
+ _readyCallback.ready(SocketOperation.Read, true);
+ }
+ catch(LocalException ex)
+ {
+ exception(ex);
+ //
+ // Mark as ready for reading so that the Ice run time will invoke read() and we can report the exception.
+ //
+ _readyCallback.ready(SocketOperation.Read, true);
+ }
+ finally
+ {
+ if(in != null)
+ {
+ try
+ {
+ in.close();
+ }
+ catch(java.io.IOException ex)
+ {
+ // Ignore.
+ }
+ }
+ }
+ }
+
+ private void runWriteThread()
+ {
+ java.io.OutputStream out = null;
+
+ try
+ {
+ synchronized(this)
+ {
+ if(_socket == null)
+ {
+ return;
+ }
+ out = _socket.getOutputStream();
+ }
+
+ boolean done = false;
+ while(!done)
+ {
+ ByteBuffer b = null;
+
+ synchronized(this)
+ {
+ while(_state == StateConnected && _exception == null && _writeBuffer.b.position() == 0)
+ {
+ try
+ {
+ wait();
+ }
+ catch(InterruptedException ex)
+ {
+ break;
+ }
+ }
+
+ if(_state != StateConnected || _exception != null)
+ {
+ done = true;
+ }
+
+ b = _writeBuffer.b; // Adopt the ByteBuffer.
+ _writeBuffer.clear();
+ }
+
+ assert(b != null && b.hasArray());
+ b.flip();
+ if(b.hasRemaining() && !done)
+ {
+ //
+ // write() blocks until all the data has been written.
+ //
+ out.write(b.array(), b.arrayOffset(), b.remaining());
+ }
+
+ // TBD: Recycle the buffer?
+
+ synchronized(this)
+ {
+ //
+ // After the write is complete, indicate whether we can accept more data.
+ //
+ _readyCallback.ready(SocketOperation.Write, _writeBuffer.b.position() < _sndSize);
+ }
+ }
+ }
+ catch(java.io.IOException ex)
+ {
+ exception(new SocketException(ex));
+ }
+ finally
+ {
+ if(out != null)
+ {
+ try
+ {
+ out.close();
+ }
+ catch(java.io.IOException ex)
+ {
+ // Ignore.
+ }
+ }
+ }
+ }
+
+ private Instance _instance;
+ private String _remoteAddr;
+ private UUID _uuid;
+ private String _connectionId;
+ private String _adapterName;
+
+ private BluetoothSocket _socket;
+
+ private static final int StateConnecting = 0;
+ private static final int StateConnected = 1;
+ private static final int StateClosed = 2;
+ private int _state;
+
+ private Thread _connectThread;
+ private Thread _readThread;
+ private Thread _writeThread;
+
+ private LocalException _exception;
+
+ private int _rcvSize;
+ private int _sndSize;
+
+ private Buffer _readBuffer;
+ private Buffer _writeBuffer;
+
+ private String _desc;
+
+ private ReadyCallback _readyCallback;
+}