зеркало из https://github.com/mozilla/pluotsorbet.git
Add bluetooth classes and remove the custom LocalDevice class
This commit is contained in:
Родитель
915f028465
Коммит
2aab4c9604
|
@ -1,8 +0,0 @@
|
|||
package javax.bluetooth;
|
||||
|
||||
public class LocalDevice {
|
||||
public static String getProperty(String property) {
|
||||
System.out.println("LocalDevice::getProperty(String) not implemented.");
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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.j2me.crypto;
|
||||
|
||||
/**
|
||||
* This exception is thrown if a requested cryptographic algorithm is not
|
||||
* available
|
||||
*/
|
||||
|
||||
public class DigestException extends com.sun.midp.crypto.DigestException {
|
||||
public DigestException(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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.j2me.crypto;
|
||||
|
||||
/**
|
||||
* Provides applications the functionality of a message digest algorithm
|
||||
*/
|
||||
|
||||
public class MessageDigest {
|
||||
/**
|
||||
* Message digest implementation.
|
||||
*/
|
||||
com.sun.midp.crypto.MessageDigest messageDigest;
|
||||
|
||||
public MessageDigest(String algorithm) throws NoSuchAlgorithmException {
|
||||
try {
|
||||
messageDigest = com.sun.midp.crypto.MessageDigest.getInstance(algorithm);
|
||||
}
|
||||
catch (com.sun.midp.crypto.NoSuchAlgorithmException e) {
|
||||
throw new NoSuchAlgorithmException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
messageDigest.reset();
|
||||
}
|
||||
|
||||
public void update(byte[] input, int offset, int len) {
|
||||
messageDigest.update(input, offset, len);
|
||||
}
|
||||
|
||||
public void digest(byte[] buf, int offset, int len) throws DigestException {
|
||||
try {
|
||||
messageDigest.digest(buf, offset, len);
|
||||
} catch (com.sun.midp.crypto.DigestException e) {
|
||||
throw new DigestException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public int getDigestLength() {
|
||||
return messageDigest.getDigestLength();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.j2me.security;
|
||||
|
||||
/**
|
||||
* BluetoothPermission access permissions.
|
||||
*/
|
||||
public class BluetoothPermission extends Permission {
|
||||
|
||||
static String STR_BLUETOOTH_CLIENT =
|
||||
"javax.microedition.io.Connector.bluetooth.client";
|
||||
static String STR_BLUETOOTH_SERVER =
|
||||
"javax.microedition.io.Connector.bluetooth.server";
|
||||
static String STR_OBEX_CLIENT =
|
||||
"javax.microedition.io.Connector.obex.client";
|
||||
static String STR_OBEX_SERVER =
|
||||
"javax.microedition.io.Connector.obex.server";
|
||||
|
||||
static public BluetoothPermission BLUETOOTH_CLIENT =
|
||||
new BluetoothPermission(STR_BLUETOOTH_CLIENT, null);
|
||||
static public BluetoothPermission BLUETOOTH_SERVER =
|
||||
new BluetoothPermission(STR_BLUETOOTH_SERVER, null);
|
||||
static public BluetoothPermission OBEX_CLIENT =
|
||||
new BluetoothPermission(STR_OBEX_CLIENT, null);
|
||||
static public BluetoothPermission OBEX_SERVER =
|
||||
new BluetoothPermission(STR_OBEX_SERVER, null);
|
||||
|
||||
public BluetoothPermission(String name, String resource)
|
||||
{
|
||||
super(name, resource);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* Authentication completion event.
|
||||
*/
|
||||
public class AuthenticationCompleteEvent extends BluetoothEvent {
|
||||
|
||||
/* ACL connection handle for which authentication has been performed. */
|
||||
private int handle;
|
||||
|
||||
/* Indicates whether the authentication succeeded. */
|
||||
private boolean success;
|
||||
|
||||
/*
|
||||
* Class constructor.
|
||||
*
|
||||
* @param aclHandle ACL connection handle
|
||||
* @param succ true if the authentication succeeded, false otherwise
|
||||
*/
|
||||
public AuthenticationCompleteEvent(int aclHandle, boolean succ) {
|
||||
handle = aclHandle;
|
||||
success = succ;
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes this event.
|
||||
*/
|
||||
public void process() {
|
||||
BluetoothStack.getInstance().onAuthenticationComplete(handle, success);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
import javax.bluetooth.*;
|
||||
import java.util.Vector;
|
||||
|
||||
/*
|
||||
* Bluetooth Control Center.
|
||||
*
|
||||
* This is a singleton which is instantiated in <code>LocalDeviceImpl</code>.
|
||||
* BCC is supposed to communicate with native application which is a central
|
||||
* authority for local Bluetooth device settings.
|
||||
*
|
||||
* To simplify porting efforts, all methods of this class work with Bluetooth
|
||||
* addresses (presented as Java strings), instead of using RemoteDeviceImpl
|
||||
* objects. Conversion between the two is performed elsewhere.
|
||||
*/
|
||||
public abstract class BCC {
|
||||
|
||||
/* Keeps the only instance of this class. */
|
||||
private static BCC instance = null;
|
||||
|
||||
/*
|
||||
* Protects the constructor to prevent unauthorized instantiation.
|
||||
*/
|
||||
protected BCC() {
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves instance of this class.
|
||||
*
|
||||
* @return instance of this class
|
||||
*/
|
||||
public synchronized static BCC getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new NativeBCC();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enables Bluetooth radio and the Bluetooth protocol stack for use.
|
||||
*
|
||||
* @return true if the operation succeeded, false otherwise
|
||||
*/
|
||||
public abstract boolean enableBluetooth();
|
||||
|
||||
/*
|
||||
* Queries the power state of the Bluetooth device.
|
||||
*
|
||||
* @return <code>true</code> is the Bluetooth device is on,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public abstract boolean isBluetoothEnabled();
|
||||
|
||||
/*
|
||||
* Returns local Bluetooth address.
|
||||
*
|
||||
* @return local Bluetooth address.
|
||||
*/
|
||||
public abstract String getBluetoothAddress();
|
||||
|
||||
/*
|
||||
* Returns user-friendly name for the local device.
|
||||
*
|
||||
* @return user-friendly name for the local device, or
|
||||
* null if the name could not be retrieved
|
||||
*/
|
||||
public abstract String getFriendlyName();
|
||||
|
||||
/*
|
||||
* Retrieves the user-friendly name for specified remote device.
|
||||
*
|
||||
* @param address Bluetooth address of a remote device
|
||||
* @return name of the remote device, or
|
||||
* null if the name could not be retrieved
|
||||
*/
|
||||
public abstract String getFriendlyName(String address);
|
||||
|
||||
/*
|
||||
* Determines if the local device is in connectable mode.
|
||||
*
|
||||
* @return true if the device is connectable, false otherwise
|
||||
*/
|
||||
public abstract boolean isConnectable();
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public abstract DeviceClass getDeviceClass();
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public abstract boolean setServiceClasses(int classes);
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public abstract int getAccessCode();
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public abstract boolean setAccessCode(int accessCode);
|
||||
|
||||
/*
|
||||
* Checks if the local device has a bond with a remote device.
|
||||
*
|
||||
* @param address Bluetooth address of a remote device
|
||||
* @return true if the two devices were paired, false otherwise
|
||||
*/
|
||||
public abstract boolean isPaired(String address);
|
||||
|
||||
/*
|
||||
* Checks if a remote device was authenticated.
|
||||
*
|
||||
* @param address Bluetooth address of a remote device
|
||||
* @return true if the device was authenticated, false otherwise
|
||||
*/
|
||||
public abstract boolean isAuthenticated(String address);
|
||||
|
||||
/*
|
||||
* Checks if a remote device is trusted (authorized for all services).
|
||||
*
|
||||
* @param address Bluetooth address of a remote device
|
||||
* @return true if the device is trusted, false otherwise
|
||||
*/
|
||||
public abstract boolean isTrusted(String address);
|
||||
|
||||
/*
|
||||
* Checks if connections to a remote device are encrypted.
|
||||
*
|
||||
* @param address Bluetooth address of the remote device
|
||||
* @return true if connections to the device are encrypted, false otherwise
|
||||
*/
|
||||
public abstract boolean isEncrypted(String address);
|
||||
|
||||
/*
|
||||
* Retrieves PIN code to use for pairing with a remote device. If the
|
||||
* PIN code is not known, PIN entry dialog is displayed.
|
||||
*
|
||||
* @param address the Bluetooth address of the remote device
|
||||
* @return string containing the PIN code
|
||||
*/
|
||||
public abstract String getPasskey(String address);
|
||||
|
||||
/*
|
||||
* Initiates pairing with a remote device.
|
||||
*
|
||||
* @param address the Bluetooth address of the device with which to pair
|
||||
* @param pin an array containing the PIN code
|
||||
* @return true if the device was authenticated, false otherwise
|
||||
*/
|
||||
public abstract boolean bond(String address, String pin);
|
||||
|
||||
/*
|
||||
* Authenticates remote device.
|
||||
*
|
||||
* @param address Bluetooth address of a remote device
|
||||
* @return true if the device was authenticated, false otherwise
|
||||
*/
|
||||
public abstract boolean authenticate(String address);
|
||||
|
||||
/*
|
||||
* Authorizes a Bluetooth connection.
|
||||
*
|
||||
* @param address the Bluetooth address of the remote device
|
||||
* @param handle handle for the service record of the srvice the remote
|
||||
* device is trying to access
|
||||
* @return true if authorization succeeded, false otherwise
|
||||
*/
|
||||
public abstract boolean authorize(String address, int handle);
|
||||
|
||||
/*
|
||||
* Enables or disables encryption of data exchanges.
|
||||
*
|
||||
* @param address the Bluetooth address of the remote device
|
||||
* @param enable specifies whether the encryption should be enabled
|
||||
* @return true if the encryption has been changed, false otherwise
|
||||
*/
|
||||
public abstract boolean encrypt(String address, boolean enable);
|
||||
|
||||
/*
|
||||
* Returns the list of preknown devices.
|
||||
*
|
||||
* @return vector containing preknown devices;
|
||||
* <code>null</code> if there is no preknown devices .
|
||||
*/
|
||||
public abstract Vector getPreknownDevices();
|
||||
|
||||
/*
|
||||
* Checks if there is a connection to the remote device.
|
||||
*
|
||||
* @param address the Bluetooth address of the remote device
|
||||
* @return true if connection is established with the remote device
|
||||
*/
|
||||
public abstract boolean isConnected(String address);
|
||||
|
||||
/*
|
||||
* Registers a new connection to a remote device.
|
||||
* For the real mode makes nothing currently.
|
||||
*
|
||||
* @param address the Bluetooth address of the remote device
|
||||
*/
|
||||
public void addConnection(String address) {}
|
||||
|
||||
/*
|
||||
* Unregisters an existing connection to a remote device.
|
||||
* For the real mode makes nothing currently.
|
||||
*
|
||||
* @param address the Bluetooth address of the remote device
|
||||
*/
|
||||
public void removeConnection(String address) {}
|
||||
|
||||
}
|
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import com.sun.jsr082.obex.ObexPacketStream;
|
||||
import com.sun.jsr082.obex.ObexTransport;
|
||||
|
||||
import javax.bluetooth.RemoteDevice;
|
||||
import javax.bluetooth.BluetoothConnectionException;
|
||||
import javax.microedition.io.Connection;
|
||||
import javax.microedition.io.Connector;
|
||||
|
||||
/*
|
||||
* Base class for all bluetooth connections.
|
||||
*/
|
||||
abstract public class BluetoothConnection {
|
||||
|
||||
/* Keeps requested connection details. */
|
||||
protected BluetoothUrl url;
|
||||
|
||||
/* Keeps open mode. */
|
||||
protected int mode;
|
||||
|
||||
/* true if this connection was authorized, false otherwise. */
|
||||
private boolean authorized;
|
||||
|
||||
/* true if this connection has requested encryption and is encrypted. */
|
||||
private boolean encrypted;
|
||||
|
||||
/* Remote device for this connection. */
|
||||
private RemoteDevice remoteDevice;
|
||||
|
||||
/*
|
||||
* Retrieves <code>BluetoothConnection</code> from given one.
|
||||
* Connection given is supposed to be either Bluetooth connection
|
||||
* or a connection that uses Bluetooth as transport. All involved
|
||||
* connections supposed to be open.
|
||||
*
|
||||
* @param conn the connection to extract Bluetooth connection from
|
||||
* @return proper <code>BluetoothConnection</code> instance
|
||||
* @throws IllegalArgumentException if connection is neither an instance
|
||||
* of BluetoothConnection, nor uses one as transport.
|
||||
* @throws IOException if connection given or transport is closed or
|
||||
* transport is invalid.
|
||||
*/
|
||||
public static BluetoothConnection getConnection(Connection conn)
|
||||
throws IOException {
|
||||
if (conn == null) {
|
||||
throw new NullPointerException("Null connection specified.");
|
||||
}
|
||||
|
||||
if (conn instanceof ObexPacketStream) {
|
||||
conn = ((ObexPacketStream)conn).getTransport();
|
||||
}
|
||||
|
||||
if (!(conn instanceof BluetoothConnection)) {
|
||||
throw new IllegalArgumentException("The specified connection " +
|
||||
"is not a Bluetooth connection.");
|
||||
}
|
||||
|
||||
BluetoothConnection btConn = (BluetoothConnection)conn;
|
||||
btConn.checkOpen();
|
||||
return btConn;
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a new instance of this class.
|
||||
*
|
||||
* @param url connection url
|
||||
* @param mode I/O access mode server side otherwise it's false
|
||||
*/
|
||||
protected BluetoothConnection(BluetoothUrl url, int mode) {
|
||||
// IMPL_NOTE: find proper place; the intent here is to start EmulationPolling
|
||||
// and SDPServer prior to create a user's notifier
|
||||
SDDB.getInstance();
|
||||
|
||||
this.url = url;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns remote device for this connection.
|
||||
*
|
||||
* @return <code>RemoteDevice</code> object for this connection
|
||||
* @throws IOException if this connection is closed
|
||||
*/
|
||||
public RemoteDevice getRemoteDevice() throws IOException {
|
||||
checkOpen();
|
||||
return remoteDevice;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns Bluetooth address of the remote device for this connection.
|
||||
*
|
||||
* @return Bluetooth address of the remote device
|
||||
*/
|
||||
public abstract String getRemoteDeviceAddress();
|
||||
|
||||
/*
|
||||
* Retrieves reference to the remote device for this connection.
|
||||
*/
|
||||
protected void setRemoteDevice() {
|
||||
remoteDevice = DiscoveryAgentImpl.getInstance().
|
||||
getRemoteDevice(getRemoteDeviceAddress());
|
||||
BCC.getInstance().addConnection(getRemoteDeviceAddress());
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes reference to the remote device.
|
||||
*/
|
||||
protected void resetRemoteDevice() {
|
||||
if (encrypted) {
|
||||
encrypt(false);
|
||||
}
|
||||
remoteDevice = null;
|
||||
BCC.getInstance().removeConnection(getRemoteDeviceAddress());
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines if this connection is closed.
|
||||
*
|
||||
* @return true if this connection is closed, false otherwise
|
||||
*/
|
||||
public boolean isClosed() {
|
||||
return remoteDevice == null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines whether this connection represents the server side,
|
||||
* i.e. this connection was created by a notifier in acceptAndOpen().
|
||||
*
|
||||
* @return true if this connection is a server-side connection,
|
||||
* false otherwise
|
||||
*/
|
||||
public boolean isServerSide() {
|
||||
return url.isServer;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the authorization state of this connection.
|
||||
*
|
||||
* @return true if this connection has been authorized, false otherwise
|
||||
*/
|
||||
public boolean isAuthorized() {
|
||||
return authorized;
|
||||
}
|
||||
|
||||
/*
|
||||
* Authorizes this connection. It is assumed that the remote device has
|
||||
* previously been authenticated. This connection must represent the server
|
||||
* side, i.e. isServer() should return true.
|
||||
*
|
||||
* @return true if the operation succeeded, false otherwise
|
||||
*/
|
||||
public boolean authorize() {
|
||||
authorized = BCC.getInstance().authorize(
|
||||
remoteDevice.getBluetoothAddress(), getServiceRecordHandle());
|
||||
return authorized;
|
||||
}
|
||||
|
||||
/*
|
||||
* Changes encryption for this connection.
|
||||
*
|
||||
* @param enable specifies whether encription should be turned on or off
|
||||
* @return true if encryption has been set as required, false otherwise
|
||||
*/
|
||||
public boolean encrypt(boolean enable) {
|
||||
if (enable == encrypted) {
|
||||
return true;
|
||||
}
|
||||
BCC.getInstance().encrypt(remoteDevice.getBluetoothAddress(), enable);
|
||||
if (remoteDevice.isEncrypted()) {
|
||||
if (enable) {
|
||||
encrypted = true;
|
||||
return true;
|
||||
}
|
||||
encrypted = false;
|
||||
return false;
|
||||
} else {
|
||||
if (enable) {
|
||||
return false;
|
||||
}
|
||||
encrypted = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns handle for the service record of the service this connection
|
||||
* is attached to. Valid for server-side (incoming) connections only.
|
||||
*
|
||||
* @return service record handle, or 0 if the handle is not available
|
||||
*/
|
||||
protected int getServiceRecordHandle() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if this connection is open.
|
||||
*
|
||||
* @throws IOException if this connection is closed
|
||||
*/
|
||||
protected void checkOpen() throws IOException {
|
||||
if (isClosed()) {
|
||||
throw new IOException("Connection is closed.");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs security checks, such as authentication, authorization, and
|
||||
* encryption setup.
|
||||
*
|
||||
* @throws BluetoothConnectionException when failed
|
||||
*/
|
||||
protected void checkSecurity()
|
||||
throws BluetoothConnectionException, IOException {
|
||||
if (url.authenticate) {
|
||||
if (!remoteDevice.authenticate()) {
|
||||
throw new BluetoothConnectionException(
|
||||
BluetoothConnectionException.SECURITY_BLOCK,
|
||||
"Authentication failed.");
|
||||
}
|
||||
}
|
||||
if (url.authorize) {
|
||||
if (!remoteDevice.authorize((Connection)this)) {
|
||||
throw new BluetoothConnectionException(
|
||||
BluetoothConnectionException.SECURITY_BLOCK,
|
||||
"Authorization failed.");
|
||||
}
|
||||
}
|
||||
if (url.encrypt) {
|
||||
if (!remoteDevice.encrypt((Connection)this, true)) {
|
||||
throw new BluetoothConnectionException(
|
||||
BluetoothConnectionException.SECURITY_BLOCK,
|
||||
"Encryption failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks read access.
|
||||
*
|
||||
* @throws IOException if open mode does not permit read access
|
||||
*/
|
||||
protected void checkReadMode() throws IOException {
|
||||
if ((mode & Connector.READ) == 0) {
|
||||
throw new IOException("Invalid mode: " + mode);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks write access.
|
||||
*
|
||||
* @throws IOException if open mode does not permit write access
|
||||
*/
|
||||
protected void checkWriteMode() throws IOException {
|
||||
if ((mode & Connector.WRITE) == 0) {
|
||||
throw new IOException("Invalid mode: " + mode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* Represents Bluetooth stack event, such as inquiry completion, etc.
|
||||
*/
|
||||
abstract class BluetoothEvent {
|
||||
|
||||
public static String eventName = null;
|
||||
|
||||
/*
|
||||
* Called immediately upon event retrieval. The default behavior is to
|
||||
* enqueue this event into the Dispatcher's event storage.
|
||||
*/
|
||||
public void dispatch() {
|
||||
Dispatcher.enqueue(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes this event.
|
||||
*/
|
||||
abstract public void process();
|
||||
|
||||
|
||||
public String toString() {
|
||||
return ("Event: " + eventName);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.microedition.io.Connection;
|
||||
import javax.bluetooth.ServiceRecord;
|
||||
import javax.bluetooth.ServiceRegistrationException;
|
||||
import javax.bluetooth.DataElement;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/*
|
||||
* Base class for all bluetooth notifiers.
|
||||
*/
|
||||
public abstract class BluetoothNotifier implements Connection {
|
||||
|
||||
/* Flag to identify if this notifier is closed. */
|
||||
protected boolean isClosed = false;
|
||||
|
||||
/* Bluetooth url this notifier created with. */
|
||||
protected BluetoothUrl url;
|
||||
|
||||
/* Service record that describes represented service. */
|
||||
protected ServiceRecordImpl serviceRec = null;
|
||||
|
||||
/* Keeps open mode. */
|
||||
protected int mode;
|
||||
|
||||
/*
|
||||
* Class constructor.
|
||||
*
|
||||
* @param url server connection string this notifier was created with
|
||||
* @param mode I/O access mode
|
||||
*/
|
||||
protected BluetoothNotifier(BluetoothUrl url, int mode) {
|
||||
// IMPL_NOTE: find proper place; the intent here is to start EmulationPolling
|
||||
// and SDPServer prior to create a user's notifier
|
||||
SDDB.getInstance();
|
||||
this.url = url;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves service record for this notifier.
|
||||
* It always returns the same object reference.
|
||||
*
|
||||
* @return service record associated with this notifier
|
||||
* @throws IllegalArgumentException if the notifier is closed
|
||||
*/
|
||||
ServiceRecord getServiceRecord() {
|
||||
if (isClosed) {
|
||||
throw new IllegalArgumentException("Notifier is closed.");
|
||||
}
|
||||
// IMPL_NOTE: copy should probably be returned instead of a reference,
|
||||
// but the current implementation returns reference to make TCK pass
|
||||
// return serviceRec.copy();
|
||||
return serviceRec;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stores the service record for this notifier in the local SDDB.
|
||||
* If there is no SDDB version of the service record, this method will
|
||||
* do nothing.
|
||||
*
|
||||
* @param record new service record value
|
||||
* @throws IllegalArgumentException if new record is invalid
|
||||
*
|
||||
* @throws ServiceRegistrationException if the record cannot be
|
||||
* updated successfully in the SDDB
|
||||
*/
|
||||
protected void updateServiceRecord(ServiceRecordImpl record)
|
||||
throws ServiceRegistrationException {
|
||||
ServiceRecordImpl oldRecord = serviceRec;
|
||||
serviceRec = record.copy();
|
||||
try {
|
||||
checkServiceRecord();
|
||||
} catch (ServiceRegistrationException e) {
|
||||
serviceRec = oldRecord;
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
if (SDDB.getInstance().contains(serviceRec)) {
|
||||
SDDB.getInstance().updateServiceRecord(serviceRec);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensures that the service record is valid.
|
||||
*
|
||||
* @throws ServiceRegistrationException in case described in the
|
||||
* JSR specification
|
||||
*/
|
||||
protected abstract void checkServiceRecord()
|
||||
throws ServiceRegistrationException;
|
||||
|
||||
/*
|
||||
* Compares two DataElements.
|
||||
*
|
||||
* @param first first DataElement
|
||||
* @param second second DataElement
|
||||
* @return true if elements are equal, false otherwise
|
||||
* @see javax.bluetooth.DataElement
|
||||
*/
|
||||
protected boolean compareDataElements(DataElement first,
|
||||
DataElement second) {
|
||||
boolean ret = false;
|
||||
int valueType = first.getDataType();
|
||||
if (ret = (valueType == second.getDataType())) {
|
||||
switch (valueType) {
|
||||
case DataElement.BOOL:
|
||||
ret = first.getBoolean() == second.getBoolean();
|
||||
break;
|
||||
case DataElement.U_INT_1:
|
||||
case DataElement.U_INT_2:
|
||||
case DataElement.U_INT_4:
|
||||
case DataElement.INT_1:
|
||||
case DataElement.INT_2:
|
||||
case DataElement.INT_4:
|
||||
case DataElement.INT_8:
|
||||
ret = first.getLong() == second.getLong();
|
||||
break;
|
||||
default:
|
||||
Object v1 = first.getValue();
|
||||
Object v2 = second.getValue();
|
||||
if (v1 instanceof Enumeration && v2 instanceof Enumeration) {
|
||||
Enumeration e1 = (Enumeration)v1;
|
||||
Enumeration e2 = (Enumeration)v2;
|
||||
ret = true;
|
||||
while (e1.hasMoreElements() &&
|
||||
e2.hasMoreElements() && ret) {
|
||||
ret &= e1.nextElement().equals(e2.nextElement());
|
||||
}
|
||||
ret = ret &&
|
||||
!(e1.hasMoreElements() ||
|
||||
e2.hasMoreElements());
|
||||
} else if (v1 instanceof byte[] && v2 instanceof byte[]) {
|
||||
byte[] a1 = (byte[])v1;
|
||||
byte[] a2 = (byte[])v2;
|
||||
ret = a1.length == a2.length;
|
||||
for (int i = a1.length; --i >= 0 && ret; ) {
|
||||
ret &= (a1[i] == a2[i]);
|
||||
}
|
||||
} else {
|
||||
ret = v1.equals(v2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Closes the connection. <code>Connection</code> interface implementation.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public abstract void close() throws IOException;
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
import com.sun.j2me.io.ConnectionBaseInterface;
|
||||
import com.sun.j2me.security.BluetoothPermission;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import javax.microedition.io.Connection;
|
||||
import javax.microedition.io.Connector;
|
||||
import javax.bluetooth.L2CAPConnection;
|
||||
import javax.bluetooth.BluetoothConnectionException;
|
||||
|
||||
import com.sun.j2me.app.AppPackage;
|
||||
import com.sun.j2me.main.Configuration;
|
||||
|
||||
/*
|
||||
* Provides abstract base for bluetooth protocols.
|
||||
*/
|
||||
public abstract class BluetoothProtocol implements ConnectionBaseInterface {
|
||||
/* Particular protocol type. */
|
||||
private int protocol;
|
||||
|
||||
/* Keeps set of fields specified by URL. */
|
||||
protected BluetoothUrl url = null;
|
||||
|
||||
/*
|
||||
* Constructs an instance.
|
||||
* @param protocol specifies particular protocol, must be one of <code>
|
||||
* BluetoothUrl.L2CAP, BluetoothUrl.RFCOMM, BluetoothUrl.OBEX </code>
|
||||
*/
|
||||
protected BluetoothProtocol(int protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements the <code>openPrim()</code> of
|
||||
* <code>ConnectionBaseInerface</code> and allows to get
|
||||
* connection by means of <code>Connector.open()</code>
|
||||
* call.
|
||||
*
|
||||
* @param name the target for the connection
|
||||
* @param mode I/O access mode
|
||||
* @param timeouts ignored
|
||||
*
|
||||
* @return L2CAP connection open.
|
||||
* @exception IOException if opening connection fails.
|
||||
*/
|
||||
public Connection openPrim(String name, int mode, boolean timeouts)
|
||||
throws IOException {
|
||||
return openPrimImpl(new BluetoothUrl(protocol, name), mode);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Checks permissions and opens requested connection.
|
||||
*
|
||||
* @param token security token passed by calling class
|
||||
* @param url <code>BluetoothUrl</code> instance that defines required
|
||||
* connection stringname the URL without protocol name and colon
|
||||
* @param mode connector.READ_WRITE or connector.READ or connector.WRITE
|
||||
*
|
||||
* @return a notifier in case of server connection string, open connection
|
||||
* in case of client one.
|
||||
*
|
||||
* @exception IOException if opening connection fails.
|
||||
*/
|
||||
protected Connection openPrimImpl(BluetoothUrl url, int mode)
|
||||
throws IOException {
|
||||
checkOpenMode(mode);
|
||||
checkUrl(url);
|
||||
this.url = url;
|
||||
|
||||
return url.isServer?
|
||||
serverConnection(mode):
|
||||
clientConnection(mode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensures open mode requested is READ_WRITE or READ or WRITE
|
||||
*
|
||||
* @param mode open mode to be checked
|
||||
* @exception IllegalArgumentException if mode given is invalid
|
||||
*
|
||||
* IMPL_NOTE check if other modes are needed
|
||||
*/
|
||||
private void checkOpenMode(int mode) throws IllegalArgumentException {
|
||||
if (mode != Connector.READ_WRITE &&
|
||||
mode != Connector.READ &&
|
||||
mode != Connector.WRITE) {
|
||||
throw new IllegalArgumentException("Unsupported mode: " + mode);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensures URL parameters have valid values. This implementation contains
|
||||
* common checks and is called from subclasses before making protocol
|
||||
* specific ones.
|
||||
*
|
||||
* @param url URL to check
|
||||
* @exception IllegalArgumentException if invalid url parameters found
|
||||
* @exception BluetoothConnectionException if url parameters are not
|
||||
* acceptable due to Bluetooth stack limitations
|
||||
*/
|
||||
protected void checkUrl(BluetoothUrl url)
|
||||
throws IllegalArgumentException, BluetoothConnectionException {
|
||||
|
||||
/*
|
||||
* IMPL_NOTE: revisit this code if TCK changes.
|
||||
* IllegalArgumentException seems to be right one here, not
|
||||
* BluetoothConnectionException. However TCK expects the latter
|
||||
* in several cases. Once IllegalArgumentException becomes
|
||||
* preferable this check can be placed to BluetoothUrl.
|
||||
* Questionable TCK tests:
|
||||
* bluetooth.Connector.Security.openClientTests,
|
||||
* bluetooth.Connector.Security.openServerTests
|
||||
*/
|
||||
if ((url.encrypt || url.authorize) && !url.authenticate) {
|
||||
throw new BluetoothConnectionException(
|
||||
BluetoothConnectionException.UNACCEPTABLE_PARAMS,
|
||||
"Invalid Authenticate parameter");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensures that permissions are proper and creates client side connection.
|
||||
* @param token security token if passed by caller, or <code>null</code>
|
||||
* client side connection.
|
||||
* @param mode I/O access mode
|
||||
* @return connection created, defined in subclasses
|
||||
* @exception IOException if opening connection fails.
|
||||
*/
|
||||
protected abstract Connection clientConnection(int mode)
|
||||
throws IOException;
|
||||
|
||||
/*
|
||||
* Ensures that permissions are proper and creates required notifier at
|
||||
* server side.
|
||||
* @param token security token if passed by caller, or <code>null</code>
|
||||
* @param mode I/O access mode
|
||||
* @return server notifier, defined in subclasses
|
||||
* @exception IOException if opening connection fails.
|
||||
*/
|
||||
protected abstract Connection serverConnection(int mode)
|
||||
throws IOException;
|
||||
|
||||
/*
|
||||
* Makes sure caller has the com.sun.midp permission set to "allowed".
|
||||
*
|
||||
* @param token security token of the calling class, may be null
|
||||
* @param permission requested permission ID
|
||||
*
|
||||
* @exception IOInterruptedException if another thread interrupts the
|
||||
* calling thread while this method is waiting to preempt the
|
||||
* display.
|
||||
*/
|
||||
protected void checkForPermission(BluetoothPermission permission)
|
||||
throws InterruptedIOException {
|
||||
|
||||
AppPackage app = AppPackage.getInstance();
|
||||
|
||||
try {
|
||||
app.checkForPermission(new BluetoothPermission(
|
||||
permission.getName(), url.getResourceName()));
|
||||
} catch (InterruptedException ie) {
|
||||
throw new InterruptedIOException(
|
||||
"Interrupted while trying to ask the user permission");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.bluetooth.DiscoveryAgent;
|
||||
import com.sun.jsr082.bluetooth.btl2cap.L2CAPNotifierImpl;
|
||||
import com.sun.jsr082.bluetooth.btspp.BTSPPNotifierImpl;
|
||||
|
||||
/*
|
||||
* Bluetooth connect-anytime services support class.
|
||||
*/
|
||||
public class BluetoothPush {
|
||||
|
||||
/* 'authenticated' AllowedSender parameter. */
|
||||
private static final String AUTHENTICATED = ";AUTHENTICATED";
|
||||
|
||||
/* 'authorized' AllowedSender parameter. */
|
||||
private static final String AUTHORIZED = ";AUTHORIZED";
|
||||
|
||||
/* 'blacklist' AllowedSender parameter. */
|
||||
private static final String BLACKLIST = ";BLACKLIST=";
|
||||
|
||||
/* Service record serializer instance. */
|
||||
private static ServiceRecordSerializer srs = new ServiceRecordSerializer();
|
||||
|
||||
/*
|
||||
* Checks if the specified URL is valid.
|
||||
*
|
||||
* @param url URL to verify
|
||||
* @throws IllegalArgumentException if the URL is malformed
|
||||
*/
|
||||
public static void verifyUrl(String url) {
|
||||
BluetoothUrl btUrl = new BluetoothUrl(url);
|
||||
if ((btUrl.encrypt || btUrl.authorize) && !btUrl.authenticate) {
|
||||
throw new IllegalArgumentException(
|
||||
"'authenticate=true' parameter is required.");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if the specified AllowedSender field is valid.
|
||||
*
|
||||
* @param filter filter to verify
|
||||
* @throws IllegalArgumentException if the filter is malformed
|
||||
*/
|
||||
public static void verifyFilter(String filter) {
|
||||
if (filter.length() == 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
filter = filter.toUpperCase();
|
||||
int i = 0;
|
||||
while (i < filter.length() && i < 12) {
|
||||
char c = filter.charAt(i++);
|
||||
if (c == '*' || c == '?') {
|
||||
continue;
|
||||
}
|
||||
if (c == ';') {
|
||||
i--;
|
||||
if (i == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid Bluetooth address.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid Bluetooth address.");
|
||||
}
|
||||
}
|
||||
filter = filter.substring(i);
|
||||
if (filter.length() == 0) {
|
||||
return;
|
||||
}
|
||||
if (filter.startsWith(AUTHENTICATED)) {
|
||||
filter = filter.substring(AUTHENTICATED.length());
|
||||
} else if (filter.startsWith(AUTHORIZED)) {
|
||||
filter = filter.substring(AUTHORIZED.length());
|
||||
}
|
||||
if (filter.length() == 0) {
|
||||
return;
|
||||
}
|
||||
if (!filter.startsWith(BLACKLIST)) {
|
||||
throw new IllegalArgumentException("Invalid parameter.");
|
||||
}
|
||||
filter = filter.substring(BLACKLIST.length());
|
||||
if (filter.length() == 0) {
|
||||
throw new IllegalArgumentException("Invalid blacklist.");
|
||||
}
|
||||
int count = 0;
|
||||
while (true) {
|
||||
if (++count > 1024) {
|
||||
throw new IllegalArgumentException("Blacklist too long.");
|
||||
}
|
||||
i = 0;
|
||||
while (i < filter.length() && i < 12) {
|
||||
char c = filter.charAt(i++);
|
||||
if (c == '*' || c == '?') {
|
||||
continue;
|
||||
}
|
||||
if (c == ';') {
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid blacklist address.");
|
||||
}
|
||||
}
|
||||
filter = filter.substring(i);
|
||||
if (filter.length() == 0) {
|
||||
return;
|
||||
}
|
||||
if (filter.charAt(0) != ';' || filter.length() == 1) {
|
||||
throw new IllegalArgumentException("Invalid blacklist.");
|
||||
}
|
||||
filter = filter.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Registers URL within Bluetooth push subsytem.
|
||||
*
|
||||
* @param url URL to register
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public static void registerUrl(String url) throws IOException {
|
||||
ServiceRecordImpl record = null;
|
||||
String protocol = url.substring(0, url.indexOf(':')).toUpperCase();
|
||||
if (protocol.equals("BTL2CAP")) {
|
||||
record = L2CAPNotifierImpl.createServiceRecord(url);
|
||||
} else if (protocol.equals("BTSPP")) {
|
||||
record = BTSPPNotifierImpl.createServiceRecord(url);
|
||||
} else if (protocol.equals("BTGOEP")) {
|
||||
record = BTSPPNotifierImpl.createServiceRecord(url);
|
||||
} else {
|
||||
throw new RuntimeException("Unsupported Bluetooth protocol.");
|
||||
}
|
||||
record.setHandle(0);
|
||||
if (!BCC.getInstance().isBluetoothEnabled() &&
|
||||
!BCC.getInstance().enableBluetooth()) {
|
||||
throw new IOException("Bluetooth radio is not enabled.");
|
||||
}
|
||||
if (!registerUrl(url, srs.serialize(record))) {
|
||||
throw new IOException("Error registering Bluetooth URL.");
|
||||
}
|
||||
if (BCC.getInstance().getAccessCode() != DiscoveryAgent.GIAC) {
|
||||
BCC.getInstance().setAccessCode(DiscoveryAgent.GIAC);
|
||||
}
|
||||
// get the emulation services up and running
|
||||
SDDB.getInstance();
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves service record associated with a service maintained by
|
||||
* Bluetooth push subsytem.
|
||||
*
|
||||
* @param notifier Bluetooth notifier to be used with the service record
|
||||
* @param url URL used during push entry registration
|
||||
* @return service record instance
|
||||
*/
|
||||
public static ServiceRecordImpl getServiceRecord(BluetoothNotifier notifier,
|
||||
String url) {
|
||||
return SDDB.getInstance().getServiceRecord(getRecordHandle(url),
|
||||
notifier);
|
||||
}
|
||||
|
||||
/*
|
||||
* Registers URL within Bluetooth push subsystem.
|
||||
*
|
||||
* @param url URL to register
|
||||
* @param data serialized service record created from the URL
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
private native static boolean registerUrl(String url, byte[] data);
|
||||
|
||||
/*
|
||||
* Retrieves service record handle for a service maintained by
|
||||
* Bluetooth push subsytem.
|
||||
*
|
||||
* @param url URL used during push entry registration
|
||||
* @return service record handle
|
||||
*/
|
||||
private native static int getRecordHandle(String url);
|
||||
|
||||
}
|
|
@ -0,0 +1,680 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
import javax.bluetooth.BluetoothStateException;
|
||||
import javax.bluetooth.DeviceClass;
|
||||
import javax.bluetooth.DiscoveryListener;
|
||||
import javax.bluetooth.RemoteDevice;
|
||||
|
||||
/*
|
||||
* Represents native Bluetooth stack provided by the system.
|
||||
* Provides services such as device and service discovery.
|
||||
*/
|
||||
public abstract class BluetoothStack {
|
||||
|
||||
private static class BTInstanceHolder
|
||||
{
|
||||
// Note to porting engineer: please replace the class name with
|
||||
// the one you intend to use on the target platform.
|
||||
private final static BluetoothStack INSTANCE = new JavacallBluetoothStack();
|
||||
}
|
||||
|
||||
/* Instance handle of the native porting layer class. */
|
||||
private int nativeInstance = 0;
|
||||
|
||||
/* Listener where discovery events are reported to. */
|
||||
private DiscoveryListener discListener = null;
|
||||
|
||||
/* Contains remote name request results. */
|
||||
private Hashtable nameResults = new Hashtable();
|
||||
|
||||
/* Contains authentication request results. */
|
||||
private Hashtable authenticateResults = new Hashtable();
|
||||
|
||||
/* Contains set encryption request results. */
|
||||
private Hashtable encryptResults = new Hashtable();
|
||||
|
||||
/* Timeout value in milliseconds for friendly name retrieval. */
|
||||
private final long ASK_FRIENDLY_NAME_TIMEOUT = 0;
|
||||
|
||||
/* Timeout value in milliseconds for authentication. */
|
||||
private final long AUTHENTICATE_TIMEOUT = 0;
|
||||
|
||||
/* Timeout value in milliseconds for setting encryption. */
|
||||
private final long ENCRYPT_TIMEOUT = 0;
|
||||
|
||||
/* Keeps the count of pending requests. */
|
||||
int pollRequests = 0;
|
||||
|
||||
/*
|
||||
* Contains results of ongoing inquiry to avoid reporting the same
|
||||
* inquiry result twice.
|
||||
*/
|
||||
Vector inquiryHistory = new Vector();
|
||||
|
||||
/*
|
||||
* Class constructor.
|
||||
*/
|
||||
protected BluetoothStack() {
|
||||
if (!initialize()) {
|
||||
throw new RuntimeException(
|
||||
"Failed to initialize Bluetooth");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocates native resources.
|
||||
*/
|
||||
private native boolean initialize();
|
||||
|
||||
/*
|
||||
* Releases native resources.
|
||||
*/
|
||||
protected native void finalize();
|
||||
|
||||
/*
|
||||
* Returns a BluetoothStack object.
|
||||
*
|
||||
* @return an instance of BluetoothStack subclass
|
||||
*/
|
||||
public synchronized static BluetoothStack getInstance()
|
||||
{
|
||||
return BTInstanceHolder.INSTANCE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a BluetoothStack object and guarantees that Bluetooth
|
||||
* radio is on.
|
||||
* @return an instance of BluetoothStack subclass
|
||||
* @throws BluetoothStateException if BluetoothStack is off and
|
||||
* cannot be turned on.
|
||||
*/
|
||||
public synchronized static BluetoothStack getEnabledInstance()
|
||||
throws BluetoothStateException {
|
||||
BluetoothStack instance = getInstance();
|
||||
if (!instance.isEnabled() && !instance.enable()) {
|
||||
throw new BluetoothStateException("Failed turning Bluetooth on");
|
||||
}
|
||||
// intent here is launching EmulationPolling and SDPServer
|
||||
// in emulation mode
|
||||
//???? com.sun.jsr082.bluetooth.SDDB.getInstance();
|
||||
return instance;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if the Bluetooth radio is enabled.
|
||||
*
|
||||
* @return true if Bluetooth is enabled, false otherwise
|
||||
*/
|
||||
public native boolean isEnabled();
|
||||
|
||||
/*
|
||||
* Enables Bluetooth radio.
|
||||
*
|
||||
* @return true if Bluetooth is enabled, false otherwise
|
||||
*/
|
||||
public native boolean enable();
|
||||
|
||||
/*
|
||||
* Returns Bluetooth address of the local device.
|
||||
*
|
||||
* @return Bluetooth address of the local device, or null if
|
||||
* the address could not be retrieved
|
||||
*/
|
||||
public native String getLocalAddress();
|
||||
|
||||
/*
|
||||
* Returns user-friendly name for the local device.
|
||||
*
|
||||
* @return User-friendly name for the local device, or null if
|
||||
* the name could not be retrieved
|
||||
*/
|
||||
public native String getLocalName();
|
||||
|
||||
/*
|
||||
* Returns class of device including service classes.
|
||||
*
|
||||
* @return class of device value, or -1 if the information could not
|
||||
* be retrieved
|
||||
*/
|
||||
public native int getDeviceClass();
|
||||
|
||||
/*
|
||||
* Sets major service class bits of the device.
|
||||
*
|
||||
* @param classes an integer whose binary representation indicates the major
|
||||
* service class bits that should be set
|
||||
* @return true if the operation succeeded, false otherwise
|
||||
*/
|
||||
public native boolean setServiceClasses(int classes);
|
||||
|
||||
/*
|
||||
* Retrieves the inquiry access code that the local Bluetooth device is
|
||||
* scanning for during inquiry scans.
|
||||
*
|
||||
* @return inquiry access code, or -1 if the information could not
|
||||
* be retrieved
|
||||
*/
|
||||
public native int getAccessCode();
|
||||
|
||||
/*
|
||||
* Sets the inquiry access code that the local Bluetooth device is
|
||||
* scanning for during inquiry scans.
|
||||
*
|
||||
* @param accessCode inquiry access code to be set (valid values are in the
|
||||
* range 0x9e8b00 to 0x9e8b3f), or 0 to take the device out of
|
||||
* discoverable mode
|
||||
* @return true if the operation succeeded, false otherwise
|
||||
*/
|
||||
public native boolean setAccessCode(int accessCode);
|
||||
|
||||
/*
|
||||
* Places the device into inquiry mode.
|
||||
*
|
||||
* @param accessCode the type of inquiry
|
||||
* @param listener the event listener that will receive discovery events
|
||||
* @return true if the inquiry was started, false otherwise
|
||||
*/
|
||||
public boolean startInquiry(int accessCode, DiscoveryListener listener) {
|
||||
if (discListener != null || listener == null) {
|
||||
return false;
|
||||
}
|
||||
discListener = listener;
|
||||
if (startInquiry(accessCode)) {
|
||||
inquiryHistory.removeAllElements();
|
||||
startPolling();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes the device from inquiry mode.
|
||||
*
|
||||
* @param listener the listener that is receiving inquiry events
|
||||
* @return true if the inquiry was canceled, false otherwise
|
||||
*/
|
||||
public boolean cancelInquiry(DiscoveryListener listener) {
|
||||
if (discListener != listener) {
|
||||
return false;
|
||||
}
|
||||
if (cancelInquiry()) {
|
||||
stopPolling();
|
||||
discListener = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves friendly name from a remote device synchronously.
|
||||
*
|
||||
* @param addr remote device address
|
||||
* @return friendly name of the remote device, or <code>null</code>
|
||||
* if the name could not be retrieved
|
||||
*/
|
||||
public String askFriendlyNameSync(String addr) {
|
||||
if (!askFriendlyName(addr)) {
|
||||
return null;
|
||||
}
|
||||
nameResults.remove(addr);
|
||||
startPolling();
|
||||
return (String)waitResult(nameResults, addr,
|
||||
ASK_FRIENDLY_NAME_TIMEOUT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs remote device authentication synchronously.
|
||||
*
|
||||
* @param addr remote device address
|
||||
* @return <code>true</code> if authentication was successful,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
public boolean authenticateSync(String addr) {
|
||||
if (!authenticate(addr)) {
|
||||
return false;
|
||||
}
|
||||
int handle = getHandle(addr);
|
||||
authenticateResults.remove(new Integer(handle));
|
||||
startPolling();
|
||||
Boolean result = (Boolean)waitResult(authenticateResults,
|
||||
new Integer(handle), AUTHENTICATE_TIMEOUT);
|
||||
if (result == null) {
|
||||
return false;
|
||||
}
|
||||
return result.booleanValue();
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets encryption mode synchronously.
|
||||
*
|
||||
* @param addr remote device address
|
||||
* @param enable <code>true</code> if the encryption needs to be enabled,
|
||||
* <code>false</code> otherwise
|
||||
* @return <code>true</code> if authentication was successful,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
public boolean encryptSync(String addr, boolean enable) {
|
||||
if (!encrypt(addr, enable)) {
|
||||
return false;
|
||||
}
|
||||
int handle = getHandle(addr);
|
||||
encryptResults.remove(new Integer(handle));
|
||||
startPolling();
|
||||
Boolean result = (Boolean)waitResult(encryptResults,
|
||||
new Integer(handle), ENCRYPT_TIMEOUT);
|
||||
if (result == null) {
|
||||
return false;
|
||||
}
|
||||
return result.booleanValue();
|
||||
}
|
||||
|
||||
/*
|
||||
* Starts a supplementary polling thread.
|
||||
*/
|
||||
public synchronized void startPolling() {
|
||||
pollRequests++;
|
||||
PollingThread.resume();
|
||||
}
|
||||
|
||||
/*
|
||||
* Cancels event polling for one request. Polling thread will continue to
|
||||
* run unless there are no other pending requests.
|
||||
*/
|
||||
public synchronized void stopPolling() {
|
||||
pollRequests--;
|
||||
if (pollRequests > 0) {
|
||||
return;
|
||||
}
|
||||
PollingThread.suspend();
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks for Bluetooth events and processes them.
|
||||
*/
|
||||
public void pollEvents() {
|
||||
while (checkEvents(null)) {
|
||||
BluetoothEvent event = retrieveEvent(null);
|
||||
if (event != null) {
|
||||
event.dispatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves Bluetooth event.
|
||||
*
|
||||
* @param handle event handle data
|
||||
* @return a Bluetooth event object
|
||||
*/
|
||||
protected abstract BluetoothEvent retrieveEvent(Object handle);
|
||||
|
||||
/*
|
||||
* Called when an inquiry request is completed.
|
||||
*
|
||||
* @param success indicates whether inquiry completed successfully
|
||||
*/
|
||||
void onInquiryComplete(boolean success) {
|
||||
if (discListener == null) {
|
||||
return;
|
||||
}
|
||||
stopPolling();
|
||||
discListener = null;
|
||||
inquiryHistory.removeAllElements();
|
||||
int type = success ? DiscoveryListener.INQUIRY_COMPLETED :
|
||||
DiscoveryListener.INQUIRY_ERROR;
|
||||
DiscoveryAgentImpl.getInstance().inquiryCompleted(type);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when an inquiry result is obtained.
|
||||
*
|
||||
* @param result inquiry result object
|
||||
*/
|
||||
void onInquiryResult(InquiryResult result) {
|
||||
if (discListener == null) {
|
||||
return;
|
||||
}
|
||||
String addr = result.getAddress();
|
||||
Enumeration e = inquiryHistory.elements();
|
||||
while (e.hasMoreElements()) {
|
||||
InquiryResult oldResult = (InquiryResult)e.nextElement();
|
||||
if (oldResult.getAddress().equals(addr)) {
|
||||
// inquiry result is already in our possession
|
||||
return;
|
||||
}
|
||||
}
|
||||
inquiryHistory.addElement(result);
|
||||
RemoteDevice dev
|
||||
= DiscoveryAgentImpl.getInstance().getRemoteDevice(addr);
|
||||
DiscoveryAgentImpl.getInstance().addCachedDevice(addr);
|
||||
discListener.deviceDiscovered(dev, result.getDeviceClass());
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a name retrieval request is completed.
|
||||
*
|
||||
* @param addr Bluetooth address of a remote device
|
||||
* @param name friendly name of the device
|
||||
*/
|
||||
void onNameRetrieve(String addr, String name) {
|
||||
stopPolling();
|
||||
putResult(nameResults, addr, name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when an authentication request is completed.
|
||||
*
|
||||
* @param handle connection handle for an ACL connection
|
||||
* @param result indicates whether the operation was successful
|
||||
*/
|
||||
void onAuthenticationComplete(int handle, boolean result) {
|
||||
stopPolling();
|
||||
putResult(authenticateResults, new Integer(handle),
|
||||
new Boolean(result));
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a set encryption request is completed.
|
||||
*
|
||||
* @param handle connection handle for an ACL connection
|
||||
* @param result indicates whether the operation was successful
|
||||
*/
|
||||
void onEncryptionChange(int handle, boolean result) {
|
||||
stopPolling();
|
||||
putResult(encryptResults, new Integer(handle), new Boolean(result));
|
||||
}
|
||||
|
||||
/*
|
||||
* Puts result value into hastable and notifies threads waiting for the
|
||||
* result to appear.
|
||||
*
|
||||
* @param hashtable <code>Hashtable</code> object where the result will be
|
||||
* stored
|
||||
* @param key key identifying the result
|
||||
* @param value value of the result
|
||||
*/
|
||||
private void putResult(Hashtable hashtable, Object key, Object value) {
|
||||
synchronized (hashtable) {
|
||||
hashtable.put(key, value);
|
||||
hashtable.notify();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Waits for the specified key to appear in the given hastable. If the key
|
||||
* does not appear within the timeout specified, <code>null</code> value is
|
||||
* returned.
|
||||
*
|
||||
* @param hashtable <code>Hashtable</code> object where the key is expected
|
||||
* @param key the key expected to appear in the hastable
|
||||
* @param timeout timeout value in milliseconds
|
||||
* @return <code>Object</code> corresponding to the given key
|
||||
*/
|
||||
private Object waitResult(Hashtable hashtable, Object key, long timeout) {
|
||||
synchronized (hashtable) {
|
||||
if (timeout == 0) {
|
||||
// infinite timeout
|
||||
while (true) {
|
||||
if (hashtable.containsKey(key)) {
|
||||
return hashtable.remove(key);
|
||||
}
|
||||
try {
|
||||
// wait for a new key-value pair to appear in hashtable
|
||||
hashtable.wait();
|
||||
} catch (InterruptedException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
// endTime indicates time up to which the method is allowed to run
|
||||
long endTime = System.currentTimeMillis() + timeout;
|
||||
while (true) {
|
||||
if (hashtable.containsKey(key)) {
|
||||
return hashtable.remove(key);
|
||||
}
|
||||
// update timeout value
|
||||
timeout = endTime - System.currentTimeMillis();
|
||||
if (timeout <= 0) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
// wait for a new key-value pair to appear in hashtable
|
||||
hashtable.wait(timeout);
|
||||
} catch (InterruptedException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves default ACL connection handle for the specified remote device.
|
||||
*
|
||||
* @param addr the Bluetooth address of the remote device
|
||||
* @return ACL connection handle value
|
||||
*/
|
||||
private native int getHandle(String addr);
|
||||
|
||||
/*
|
||||
* Passes device discovery request to the native porting layer.
|
||||
*
|
||||
* @param accessCode the type of inquiry
|
||||
* @return <code>true</code> if the operation was accepted,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
private native boolean startInquiry(int accessCode);
|
||||
|
||||
/*
|
||||
* Passes cancellation of device discovery request to the native porting
|
||||
* layer.
|
||||
*
|
||||
* @return <code>true</code> if the operation was accepted,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
private native boolean cancelInquiry();
|
||||
|
||||
/*
|
||||
* Passes remote device's friendly name acquisition request to the native
|
||||
* porting layer.
|
||||
*
|
||||
* @param addr Bluetooth address of the remote device
|
||||
* @return <code>true</code> if the operation was accepted,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
private native boolean askFriendlyName(String addr);
|
||||
|
||||
/*
|
||||
* Passes remote device authentication request to the native porting layer.
|
||||
*
|
||||
* @param addr Bluetooth address of the remote device
|
||||
* @return <code>true</code> if the operation was accepted,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
private native boolean authenticate(String addr);
|
||||
|
||||
/*
|
||||
* Passes connection encryption change request to the native porting layer.
|
||||
*
|
||||
* @param addr Bluetooth address of the remote device
|
||||
* @param enable <code>true</code> if the encryption needs to be enabled,
|
||||
* <code>false</code> otherwise
|
||||
* @return <code>true</code> if the operation was accepted,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
private native boolean encrypt(String addr, boolean enable);
|
||||
|
||||
/*
|
||||
* Checks if Bluetooth events are available on the native porting layer.
|
||||
*
|
||||
* @param handle interger two words length array for storing event data in;
|
||||
* the first word indicates event's minor id.
|
||||
* @return <code>true</code> if there are pending events,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
protected native boolean checkEvents(Object handle);
|
||||
|
||||
/*
|
||||
* Reads binary event data from the native porting layer. This data can
|
||||
* be interpreted differently by different subclasses of BluetoothStack.
|
||||
*
|
||||
* @param eventData event object to be filled with data
|
||||
* @return number of bytes read
|
||||
*/
|
||||
protected native boolean readData(Object eventData);
|
||||
}
|
||||
|
||||
/*
|
||||
* Supplementary thread which periodically polls Bluetooth stack for events.
|
||||
*/
|
||||
class PollingThread extends Thread {
|
||||
|
||||
/* Instance of this class. */
|
||||
private static PollingThread instance = new PollingThread();
|
||||
|
||||
/* Flag indicating if this thread should be suspended. */
|
||||
private static boolean suspended = true;
|
||||
|
||||
/* Polling interval in milliseconds. */
|
||||
private final int POLL_INTERVAL = 1000;
|
||||
|
||||
/*
|
||||
* Class constructor.
|
||||
*/
|
||||
public PollingThread() {
|
||||
}
|
||||
|
||||
/*
|
||||
* Suspends this thread.
|
||||
*/
|
||||
public static void suspend() {
|
||||
synchronized (instance) {
|
||||
suspended = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Resumes this thread.
|
||||
*/
|
||||
public static void resume() {
|
||||
try {
|
||||
instance.start();
|
||||
} catch (IllegalThreadStateException e) {
|
||||
}
|
||||
synchronized (instance) {
|
||||
suspended = false;
|
||||
instance.notify();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Execution body.
|
||||
*/
|
||||
public void run() {
|
||||
BluetoothStack stack = BluetoothStack.getInstance();
|
||||
while (true) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
if (suspended) {
|
||||
wait();
|
||||
}
|
||||
}
|
||||
stack.pollEvents();
|
||||
synchronized (this) {
|
||||
wait(POLL_INTERVAL);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Supplementary thread which dispatches Bluetooth events to user notifiers.
|
||||
*/
|
||||
class Dispatcher extends Thread {
|
||||
|
||||
/* Instance of this class. */
|
||||
private static Dispatcher instance = new Dispatcher();
|
||||
|
||||
/* Vector containing Bluetooth events awaiting to be dispatched. */
|
||||
private static Vector events = new Vector();
|
||||
|
||||
/*
|
||||
* Class constructor.
|
||||
*/
|
||||
public Dispatcher() {
|
||||
}
|
||||
|
||||
/*
|
||||
* Puts the event in the event queue.
|
||||
* It also starts event dispatcher thread if it's not running yet.
|
||||
*
|
||||
* @param event the event to be enqueued
|
||||
*/
|
||||
public static void enqueue(BluetoothEvent event) {
|
||||
try {
|
||||
instance.start();
|
||||
} catch (IllegalThreadStateException e) {
|
||||
}
|
||||
synchronized (events) {
|
||||
events.addElement(event);
|
||||
events.notify();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Execution body.
|
||||
*/
|
||||
public void run() {
|
||||
while (true) {
|
||||
BluetoothEvent event = null;
|
||||
synchronized (events) {
|
||||
if (!events.isEmpty()) {
|
||||
event = (BluetoothEvent)events.firstElement();
|
||||
events.removeElementAt(0);
|
||||
} else {
|
||||
try {
|
||||
events.wait();
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event != null) {
|
||||
event.process();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,599 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
import javax.bluetooth.UUID;
|
||||
import javax.bluetooth.BluetoothConnectionException;
|
||||
import java.util.Hashtable;
|
||||
|
||||
/*
|
||||
* Represents a bluetooth url, i.e. connection string.
|
||||
* There are two ways of usage. First one is constructing it giving
|
||||
* url string in order to parse it into a set of fields. Second one
|
||||
* is constructig it giving fields values in order to get string
|
||||
* representation. Whenever incompatible url parts are found
|
||||
* <code>IllegalArgumentException</code> is thrown.
|
||||
*/
|
||||
public class BluetoothUrl {
|
||||
/* Indicates if it is a sever connection string. */
|
||||
public boolean isServer = false;
|
||||
|
||||
/* Keeps server address for client url, "localhost" for server. */
|
||||
public String address = null;
|
||||
/* PSM for L2CAP or channel id for RFCOMM. */
|
||||
public int port = -1;
|
||||
/* Master parameter, true by default for server. */
|
||||
public boolean master = false;
|
||||
/* Encrypt parameter. */
|
||||
public boolean encrypt = false;
|
||||
/* Authenticate parameter. */
|
||||
public boolean authenticate = false;
|
||||
|
||||
/* Value to indicate L2CAP protocol. */
|
||||
public static final int L2CAP = 0;
|
||||
/* Value to indicate RFCOMM protocol. */
|
||||
public static final int RFCOMM = 1;
|
||||
/* Value to indicate OBEX protocol. */
|
||||
public static final int OBEX = 2;
|
||||
/* Value to indicate unknown protocol. */
|
||||
public static final int UNKNOWN = 3;
|
||||
/* Indicates protocol type. */
|
||||
public int protocol = UNKNOWN;
|
||||
|
||||
/* Amount of protocols supported. */
|
||||
private static final int PROTOCOLS_AMOUNT = 3;
|
||||
/*
|
||||
* Keeps protocols indicating strings.
|
||||
* Usage:
|
||||
* <code>protocolName[L2CAP]</code> to get "l2cap"
|
||||
*/
|
||||
private static final String[] protocolName =
|
||||
{ "btl2cap://", "btspp://", "btgoep://" };
|
||||
|
||||
/*
|
||||
* Keeps uuid from server connection string,
|
||||
* <code>null</code> for client's one.
|
||||
* L2CAP, RFCOMM specific.
|
||||
*/
|
||||
public String uuid = null;
|
||||
|
||||
/*
|
||||
* Name parameter of server url, <code>null</code> for client's one.
|
||||
* L2CAP, RFCOMM specific.
|
||||
*/
|
||||
public String name = null;
|
||||
|
||||
/* Url string to parse, lower case. */
|
||||
private String url;
|
||||
/*
|
||||
* Url string to parse, original case.
|
||||
* Required for correct "name" parameter parsing for it is case-sensitive.
|
||||
*/
|
||||
public String caseSensitiveUrl;
|
||||
|
||||
/* Authorize parameter. L2CAP specific. */
|
||||
public boolean authorize = false;
|
||||
/* RecieveMTU parameter. L2CAP specific. */
|
||||
public int receiveMTU = -1;
|
||||
/* TransmitMTU parameter. L2CAP specific. */
|
||||
public int transmitMTU = -1;
|
||||
|
||||
/* UUID value to create a transport for Service Discovery Protocol. */
|
||||
public static final UUID UUID_SDP = new UUID(0x0001);
|
||||
|
||||
/* Indicates if an explicit "authenticate" parameter found. */
|
||||
private boolean explicitAuthenticate = false;
|
||||
|
||||
/* Keeps server host string. */
|
||||
private static final String LOCALHOST = "localhost";
|
||||
|
||||
/* Keeps length of url. */
|
||||
private int length = 0;
|
||||
|
||||
/* Master parameter name. */
|
||||
private static final String MASTER = ";master=";
|
||||
/* Encrypt parameter name. */
|
||||
private static final String ENCRYPT = ";encrypt=";
|
||||
/* Authenticate parameter name. */
|
||||
private static final String AUTHENTICATE = ";authenticate=";
|
||||
/* Authorize parameter name. */
|
||||
private static final String AUTHORIZE = ";authorize=";
|
||||
/* TransmitMTU parameter name. */
|
||||
private static final String TRANSMITMTU = ";transmitmtu=";
|
||||
/* ReceiveMTU parameter name. */
|
||||
private static final String RECEIVEMTU = ";receivemtu=";
|
||||
/* Name parameter name. */
|
||||
private static final String NAME = ";name=";
|
||||
|
||||
/* "true" literal. */
|
||||
private static final String TRUE = "true";
|
||||
/* "false" literal. */
|
||||
private static final String FALSE = "false";
|
||||
|
||||
/* the URL parameters. */
|
||||
private Hashtable parameters;
|
||||
|
||||
/* Stub object for values in parameters hashtable.*/
|
||||
private final static Object on = new Object();
|
||||
|
||||
/* Shows whether this url is generated and validated by SDP routines. */
|
||||
private boolean isSystem = false;
|
||||
|
||||
/*
|
||||
* Constructs url object by specified url string. Constructing
|
||||
* <code>BluetoothUrl</code> in this manner is a way to parse
|
||||
* an url represented by string.
|
||||
*
|
||||
* @param urlString url string.
|
||||
*/
|
||||
public BluetoothUrl(String urlString) {
|
||||
this(UNKNOWN, urlString, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructs url object by specified protocol and url string without
|
||||
* leading protocol name and colon or if protocol is unknown by s string
|
||||
* that contains full url.
|
||||
*
|
||||
* @param protocol prootocol type, must be one of
|
||||
* <code>L2CAP, RFCOMM, OBEX, UNKNOWN</code>.
|
||||
* @param urlString whole url if <code>protocol</code> value is
|
||||
* <code>UNKNOWN</code>, a part of url string beyond
|
||||
* "protocol:" otherwise.
|
||||
*/
|
||||
public BluetoothUrl(int protocol, String urlString) {
|
||||
this(protocol, urlString, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructs url object with specified protocol, url and special system
|
||||
* token.
|
||||
* @see BluetoothUrl(int, String)
|
||||
*
|
||||
* @param protocol prootocol type
|
||||
* @param urlString URL
|
||||
* @param systemToken special object that validates this URL as system
|
||||
* if has proper value, usually it is <code>null</code>
|
||||
*/
|
||||
public BluetoothUrl(int protocol, String urlString, Object systemToken) {
|
||||
this(protocol);
|
||||
|
||||
isSystem = SDP.checkSystemToken(systemToken);
|
||||
caseSensitiveUrl = urlString;
|
||||
url = urlString.toLowerCase();
|
||||
length = url.length();
|
||||
int start;
|
||||
int separator = url.indexOf(':');
|
||||
|
||||
if (protocol == UNKNOWN) {
|
||||
// url is "PROTOCOL://ADDRESS:...", parsing protocol name
|
||||
assertTrue(separator > 0, "Cannot parse protocol name: " + url);
|
||||
start = separator + 3; // skip "://"
|
||||
String name = urlString.substring(0, start);
|
||||
|
||||
for (int i = 0; i < PROTOCOLS_AMOUNT; i++) {
|
||||
if (protocolName[i].equals(name)) {
|
||||
this.protocol = i;
|
||||
separator = url.indexOf(':', start);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// url is "//ADDRESS:...", parsing protocol name
|
||||
assertTrue(urlString.startsWith("//"),
|
||||
"address and protocol name have to be separated by //: " + url);
|
||||
// skip "//"
|
||||
start = 2;
|
||||
}
|
||||
|
||||
assertTrue(separator > start, "Cannot parse address: " + url);
|
||||
|
||||
address = url.substring(start, separator);
|
||||
start = separator + 1;
|
||||
|
||||
|
||||
if (this.protocol == L2CAP) {
|
||||
// parsing psm or uuid
|
||||
if (address.equals(LOCALHOST)) {
|
||||
isServer = true;
|
||||
// Now uuid goes till end of string or semicolon.
|
||||
separator = getSeparator(start);
|
||||
uuid = url.substring(start, separator);
|
||||
|
||||
} else {
|
||||
// Now psm goes which is represented by 4 hex digits.
|
||||
assertTrue((separator = start + 4) <= length,
|
||||
"psm has to be represented by 4 hex digits: " + url);
|
||||
port = parseInt(start, separator, 16);
|
||||
}
|
||||
|
||||
} else if (this.protocol == RFCOMM ||
|
||||
this.protocol == OBEX) {
|
||||
separator = getSeparator(start);
|
||||
if (address.equals(LOCALHOST)) {
|
||||
isServer = true;
|
||||
// Now uuid goes till end of string or semicolon.
|
||||
uuid = url.substring(start, separator);
|
||||
} else {
|
||||
// Now channel id goes which is represented by %d1-30.
|
||||
assertTrue(separator <= length,
|
||||
"channel id has to go after address: " + url);
|
||||
port = parseInt(start, separator, 10);
|
||||
}
|
||||
} else {
|
||||
separator = getSeparator(start);
|
||||
port = parseInt(start, separator, 16);
|
||||
}
|
||||
|
||||
if (isServer) {
|
||||
int length;
|
||||
assertTrue(uuid != null && (length = uuid.length()) > 0 &&
|
||||
length <= 32, "Invalid UUID");
|
||||
} else {
|
||||
checkBluetoothAddress();
|
||||
}
|
||||
|
||||
// parsing parameters
|
||||
parameters = new Hashtable();
|
||||
for (start = separator; start < length; start = parseParameter(start));
|
||||
parameters = null;
|
||||
|
||||
assertTrue(start == length, "Cannot parse the parameters: " + url);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates url that represents client connection string.
|
||||
*
|
||||
* @param protocol identifies protocol. Should be one of
|
||||
* <code>
|
||||
* BluetoothUrl.L2CAP, BluetoothUrl.RFCOMM, BluetoothUrl.OBEX
|
||||
* </code>
|
||||
*
|
||||
* @param btaddr Bluetooth address of server device.
|
||||
*
|
||||
* @param port PSM in case of L2CAP or channel id otherwise.
|
||||
*
|
||||
* @return <code>BluetoothUrl</code> instance that represents
|
||||
* desired connection string.
|
||||
*
|
||||
* @exception IllegalArgument exception if provived parameters are invalid.
|
||||
*/
|
||||
public static BluetoothUrl createClientUrl(int protocol,
|
||||
String btaddr, int port)
|
||||
throws IllegalArgumentException {
|
||||
|
||||
assertTrue(protocol != UNKNOWN && btaddr != null,
|
||||
"Either unknown protocol name or address");
|
||||
BluetoothUrl url = new BluetoothUrl(protocol);
|
||||
|
||||
url.address = btaddr.toLowerCase();
|
||||
url.checkBluetoothAddress();
|
||||
url.port = port;
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
/*
|
||||
* Universal private constructor.
|
||||
* @param protocol identifies protocol.
|
||||
* @exception IllegalArgument exception if provived parameters are invalid.
|
||||
*/
|
||||
private BluetoothUrl(int protocol) {
|
||||
assertTrue(protocol <= UNKNOWN, "Unknown protocol name: " + protocol);
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks url parts consistency and creates string representation.
|
||||
* @return string representation of the URL.
|
||||
* @exception IllegalArgumentException if URL parts are inconsistent.
|
||||
*/
|
||||
public String toString() {
|
||||
assertTrue(protocol == L2CAP ||
|
||||
protocol == RFCOMM ||
|
||||
protocol == OBEX,
|
||||
"Incorrect protocol bname: " + protocol);
|
||||
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
|
||||
buffer = new StringBuffer(getResourceName());
|
||||
buffer.append(':');
|
||||
|
||||
if (isServer) {
|
||||
buffer.append(uuid);
|
||||
buffer.append(AUTHORIZE).append(authorize ? TRUE : FALSE);
|
||||
} else {
|
||||
String portStr;
|
||||
|
||||
if (protocol == L2CAP) {
|
||||
// in case of l2cap, the psm is 4 hex digits
|
||||
portStr = Integer.toHexString(port);
|
||||
for (int pad = 4 - portStr.length(); pad > 0; pad--) {
|
||||
buffer.append('0');
|
||||
}
|
||||
|
||||
} else if (protocol == RFCOMM ||
|
||||
protocol == OBEX) {
|
||||
portStr = Integer.toString(port);
|
||||
} else {
|
||||
portStr = Integer.toString(port);
|
||||
}
|
||||
|
||||
buffer.append(portStr);
|
||||
}
|
||||
|
||||
/*
|
||||
* note: actually it's not required to add the boolean parameter if it
|
||||
* equals to false because if it is not present in the connection
|
||||
* string, this is equivalent to 'such parameter'=false.
|
||||
* But some TCK tests check the parameter is always present in
|
||||
* URL string even its value is false.
|
||||
* IMPL_NOTE: revisit this code if TCK changes.
|
||||
*/
|
||||
buffer.append(MASTER).append(master ? TRUE : FALSE);
|
||||
buffer.append(ENCRYPT).append(encrypt ? TRUE: FALSE);
|
||||
buffer.append(AUTHENTICATE).append(authenticate ? TRUE : FALSE);
|
||||
|
||||
if (receiveMTU != -1) {
|
||||
buffer.append(RECEIVEMTU).append(
|
||||
Integer.toString(receiveMTU, 10));
|
||||
}
|
||||
if (transmitMTU != -1) {
|
||||
buffer.append(TRANSMITMTU).append(
|
||||
Integer.toString(transmitMTU, 10));
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates string representation of the URL without parameters.
|
||||
* @return "PROTOCOL://ADDRESS" string.
|
||||
*/
|
||||
public String getResourceName() {
|
||||
assertTrue(protocol == L2CAP ||
|
||||
protocol == RFCOMM ||
|
||||
protocol == OBEX,
|
||||
"Incorrect protocol bname: " + protocol);
|
||||
assertTrue(address != null, "Incorrect address: "+ address);
|
||||
return protocolName[protocol] + address;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests if this URL is system one. System URL can only by created
|
||||
* by SDP server or client and is processed in special way.
|
||||
*
|
||||
* @return <code>true</code> if this url is a system one created
|
||||
* by SDP routines, <code>false</code> otherwise
|
||||
*/
|
||||
public final boolean isSystem() {
|
||||
return isSystem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks the string given is a valid Bluetooth address, which means
|
||||
* consists of 12 hexadecimal digits.
|
||||
*
|
||||
* @exception IllegalArgumentException if string given is not a valid
|
||||
* Bluetooth address
|
||||
*/
|
||||
private void checkBluetoothAddress()
|
||||
throws IllegalArgumentException {
|
||||
|
||||
String errorMessage = "Invalid Bluetooth address";
|
||||
assertTrue(address != null && address.length() == 12 &&
|
||||
address.indexOf('-') == -1, errorMessage);
|
||||
|
||||
try {
|
||||
Long.parseLong(address, 16);
|
||||
} catch (NumberFormatException e) {
|
||||
assertTrue(false, errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses parameter in url starting at given position and cheks simple
|
||||
* rules or parameters compatibility. Parameter is ";NAME=VALUE". If
|
||||
* parsing from given position or a check fails,
|
||||
* <code>IllegalArgumentException</code> is thrown.
|
||||
*
|
||||
* @param start position to start parsing at, if it does not point to
|
||||
* semicolon, parsing fails as well as instance constructing.
|
||||
* @return position number that immediately follows parsed parameter.
|
||||
* @exception IllegalArgumentException if parsing fails or incompatible
|
||||
* parameters occured.
|
||||
*/
|
||||
private int parseParameter(int start) throws IllegalArgumentException {
|
||||
assertTrue(url.charAt(start) == ';',
|
||||
"Cannot parse url parameters: " + url);
|
||||
|
||||
int separator = url.indexOf('=', start) + 1;
|
||||
assertTrue(separator > 0, "Cannot parse url parameters: " + url);
|
||||
// name is ";NAME="
|
||||
String name = url.substring(start, separator);
|
||||
|
||||
start = separator;
|
||||
separator = getSeparator(start);
|
||||
|
||||
assertTrue(!parameters.containsKey(name),
|
||||
"Duplicate parameter " + name);
|
||||
parameters.put(name, on);
|
||||
|
||||
if (name.equals(MASTER)) {
|
||||
master = parseBoolean(start, separator);
|
||||
|
||||
} else if (name.equals(ENCRYPT)) {
|
||||
encrypt = parseBoolean(start, separator);
|
||||
if (encrypt && !explicitAuthenticate) {
|
||||
authenticate = true;
|
||||
}
|
||||
|
||||
} else if (name.equals(AUTHENTICATE)) {
|
||||
authenticate = parseBoolean(start, separator);
|
||||
explicitAuthenticate = true;
|
||||
|
||||
} else if (name.equals(NAME)) {
|
||||
assertTrue(isServer, "Incorrect parameter for client: " + name);
|
||||
// this parameter is case-sensitive
|
||||
this.name = caseSensitiveUrl.substring(start, separator);
|
||||
assertTrue(checkNameFormat(this.name),
|
||||
"Incorrect name format: " + this.name);
|
||||
|
||||
} else if (name.equals(AUTHORIZE)) {
|
||||
assertTrue(isServer, "Incorrect parameter for client: " + name);
|
||||
authorize = parseBoolean(start, separator);
|
||||
if (authorize && !explicitAuthenticate) {
|
||||
authenticate = true;
|
||||
}
|
||||
|
||||
} else if (protocol == L2CAP) {
|
||||
if (name.equals(RECEIVEMTU)) {
|
||||
receiveMTU = parseInt(start, separator, 10);
|
||||
assertTrue(receiveMTU > 0,
|
||||
"Incorrect receive MTU: " + receiveMTU);
|
||||
} else if (name.equals(TRANSMITMTU)) {
|
||||
transmitMTU = parseInt(start, separator, 10);
|
||||
assertTrue(transmitMTU > 0,
|
||||
"Incorrect transmit MTU: " + transmitMTU);
|
||||
} else {
|
||||
assertTrue(false, "Unknown parameter name = " + name);
|
||||
}
|
||||
} else {
|
||||
assertTrue(false, "Unknown parameter name = " + name);
|
||||
}
|
||||
return separator;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks name format.
|
||||
* name = 1*( ALPHA / DIGIT / SP / "-" / "_")
|
||||
* The core rules from RFC 2234.
|
||||
*
|
||||
* @param name the name
|
||||
* @return <code>true</code> if the name format is valid,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
private boolean checkNameFormat(String name) {
|
||||
char[] a = name.toCharArray();
|
||||
boolean ret = a.length > 0;
|
||||
for (int i = a.length; --i >= 0 && ret;) {
|
||||
ret &= (a[i] >= 'a' && a[i] <= 'z') ||
|
||||
(a[i] >= 'A' && a[i] <= 'Z') ||
|
||||
(a[i] >= '0' && a[i] <= '9') ||
|
||||
(a[i] == '-') ||
|
||||
(a[i] == '_') ||
|
||||
(a[i] == ' ');
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves position of semicolon in the rest of url string, returning
|
||||
* length of the string if no semicolon found. It also assertd that a
|
||||
* non-empty substring starts from <code>start</code> given and ends
|
||||
* before semicolon or end of string.
|
||||
*
|
||||
* @param start position in url string to start searching at.
|
||||
*
|
||||
* @return position of first semicolon or length of url string if there
|
||||
* is no semicolon.
|
||||
*
|
||||
* @exception IllegalArgumentException if there is no non-empty substring
|
||||
* before semicolon or end of url.
|
||||
*/
|
||||
private int getSeparator(int start) {
|
||||
int separator = url.indexOf(';', start);
|
||||
if (separator < 0) {
|
||||
separator = length;
|
||||
}
|
||||
assertTrue(start < separator, "Correct separator is not found");
|
||||
|
||||
return separator;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses boolean value from url string.
|
||||
*
|
||||
* @param start position to start parsing from.
|
||||
* @param separator position that immediately follows value to parse.
|
||||
*
|
||||
* @return true if, comparing case insensitive, specified substring is
|
||||
* "TRUE", false if it is "FALSE".
|
||||
* @exception IllegalArgumentException if specified url substring is
|
||||
* neither "TRUE" nor "FALSE", case-insensitive.
|
||||
*/
|
||||
private boolean parseBoolean(int start, int separator)
|
||||
throws IllegalArgumentException {
|
||||
String value = url.substring(start, separator);
|
||||
if (value.equals(TRUE)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
assertTrue(value.equals(FALSE), "Incorrect boolean parsing: " + value);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses integer value from url string.
|
||||
*
|
||||
* @param start position to start parsing from.
|
||||
* @param separator position that immediately follows value to parse.
|
||||
* @param radix the radix to use.
|
||||
*
|
||||
* @return integer value been parsed.
|
||||
* @exception IllegalArgumentException if given string is not
|
||||
* case-insensitive "TRUE" or "FALSE".
|
||||
*/
|
||||
private int parseInt(int start, int separator, int radix)
|
||||
throws IllegalArgumentException {
|
||||
int result = -1;
|
||||
|
||||
try {
|
||||
result = Integer.parseInt(
|
||||
url.substring(start, separator), radix);
|
||||
} catch (NumberFormatException e) {
|
||||
assertTrue(false, "Incorrect int parsing: " +
|
||||
url.substring(start, separator));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Asserts that given condition is true.
|
||||
* @param condition condition to check.
|
||||
* @param details condition's description details.
|
||||
* @exception IllegalArgumentException if given condition is flase.
|
||||
*/
|
||||
private static void assertTrue(boolean condition, String details)
|
||||
throws IllegalArgumentException {
|
||||
if (!condition) {
|
||||
throw new IllegalArgumentException("unexpected parameter: " +
|
||||
details);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* Class contains Bluetooth helper methods.
|
||||
*/
|
||||
public class BluetoothUtils {
|
||||
/* Size of byte representation of Bluetooth address. */
|
||||
public static final int BTADDR_SIZE = 6;
|
||||
|
||||
/*
|
||||
* Converts Bluetooth address from byte array to string representation.
|
||||
*
|
||||
* @param btaddr 6-bytes byte array containing Bluetooth address in
|
||||
* Bluetooth byte order (little-endian)
|
||||
* @return Bluetooth address as string in
|
||||
* user-friendly byte order (big-endian) without comma separator
|
||||
* @throws IllegalArgumentException if input address is invalid
|
||||
*/
|
||||
public static String getAddressString(byte[] btaddr)
|
||||
throws IllegalArgumentException {
|
||||
final int len = btaddr.length;
|
||||
if (len != BTADDR_SIZE) {
|
||||
throw new IllegalArgumentException("Incorrect address size");
|
||||
}
|
||||
|
||||
StringBuffer sb = new StringBuffer(len * 2);
|
||||
String s;
|
||||
for (int i = (len - 1); i >= 0; i--) {
|
||||
// convert decimal to hexadecimal with leading zeroes and uppercase
|
||||
s = Integer.toHexString((btaddr[i] >> 4) & 0xF);
|
||||
sb.append(s.toUpperCase());
|
||||
s = Integer.toHexString(btaddr[i] & 0xF);
|
||||
sb.append(s.toUpperCase());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts Bluetooth address from string to byte array representation.
|
||||
*
|
||||
* @param btaddr Bluetooth address as string in
|
||||
* user-friendly byte order (big-endian) without comma separator
|
||||
* @return 6-bytes byte array containing Bluetooth address in
|
||||
* Bluetooth byte order (little-endian)
|
||||
* @throws IllegalArgumentException if input address is invalid
|
||||
*/
|
||||
public static byte[] getAddressBytes(String btaddr)
|
||||
throws IllegalArgumentException {
|
||||
final int len = btaddr.length() / 2;
|
||||
if (len != BTADDR_SIZE) {
|
||||
throw new IllegalArgumentException("Incorrect address size");
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[len];
|
||||
try {
|
||||
for (int i = 0; i < len; i++) {
|
||||
String s = btaddr.substring(i * 2, i * 2 + 2);
|
||||
bytes[len - 1 - i] = (byte)Integer.parseInt(s, 16);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Incorrect address value");
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import javax.bluetooth.DataElement;
|
||||
|
||||
/*
|
||||
* Provides ServiceAttribute transaction functionality.
|
||||
*/
|
||||
public class ClientServiceAttributeTransaction extends SDPClientTransaction {
|
||||
|
||||
/* ServiceRecordHandle (BT Spec 1.2 Vol 3 page 138). */
|
||||
int serviceRecordHandle;
|
||||
/* AttributeIDList (BT Spec 1.2 Vol 3 page 139). */
|
||||
DataElement attributeIDList;
|
||||
/* AttributeList (BT Spec 1.2 Vol 3 page 140). */
|
||||
byte[] attributes;
|
||||
|
||||
/*
|
||||
* Constructs ServiceAttributeTransaction object.
|
||||
*/
|
||||
ClientServiceAttributeTransaction(JavaSDPClient client, int transactionID,
|
||||
SDPResponseListener listener, int recordHandle,
|
||||
int[] attrSet) {
|
||||
super(client, SDPClientTransaction.SDP_SERVICE_ATTRIBUTE_REQUEST, transactionID, listener);
|
||||
serviceRecordHandle = recordHandle;
|
||||
attributeIDList = new DataElement(DataElement.DATSEQ);
|
||||
for (int i = 0; i < attrSet.length; i++) {
|
||||
attributeIDList.addElement(new DataElement(
|
||||
DataElement.U_INT_2, attrSet[i]));
|
||||
}
|
||||
parameterLength = super.client.getConnection().getReaderWriter().getDataSize(attributeIDList) + 6;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes transaction-specific parameters into the PDU.
|
||||
*
|
||||
* @throws IOException when an I/O error occurs
|
||||
*/
|
||||
void writeParameters() throws IOException {
|
||||
super.client.getConnection().getReaderWriter().writeInteger(serviceRecordHandle);
|
||||
super.client.getConnection().getReaderWriter().writeShort((short)MAX_ATTRIBUTE_BYTE_COUNT);
|
||||
super.client.getConnection().getReaderWriter().writeDataElement(attributeIDList);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads transaction-specific parameters from the PDU.
|
||||
*
|
||||
* @param length length of PDU's parameters
|
||||
* @throws IOException when an I/O error occurs
|
||||
*/
|
||||
void readParameters(int length) throws IOException {
|
||||
short byteCount = super.client.getConnection().getReaderWriter().readShort();
|
||||
byte[] data = super.client.getConnection().getReaderWriter().readBytes(byteCount);
|
||||
if (attributes == null) {
|
||||
attributes = data;
|
||||
} else {
|
||||
byte[] temp = attributes;
|
||||
attributes = new byte[temp.length + byteCount];
|
||||
System.arraycopy(temp, 0, attributes, 0, temp.length);
|
||||
System.arraycopy(data, 0, attributes, temp.length,
|
||||
byteCount);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Completes the transaction by calling corresponding listener's
|
||||
* method with the data retrieved.
|
||||
*/
|
||||
public void complete() {
|
||||
DataElement attrList;
|
||||
DataElementSerializer des = new DataElementSerializer();
|
||||
try {
|
||||
attrList = des.restore(attributes);
|
||||
} catch (IOException e) {
|
||||
listener.serviceAttributeResponse(null, null,
|
||||
ssTransID);
|
||||
return;
|
||||
}
|
||||
int size = attrList.getSize() / 2;
|
||||
if (size == 0) {
|
||||
listener.serviceAttributeResponse(null, null,
|
||||
ssTransID);
|
||||
return;
|
||||
}
|
||||
Enumeration elements = (Enumeration)attrList.getValue();
|
||||
final int[] attrIDs = new int[size];
|
||||
final DataElement[] attrValues = new DataElement[size];
|
||||
for (int i = 0; elements.hasMoreElements(); i++) {
|
||||
attrIDs[i] = (int)((DataElement)
|
||||
elements.nextElement()).getLong();
|
||||
attrValues[i] = ((DataElement)
|
||||
elements.nextElement());
|
||||
}
|
||||
listener.serviceAttributeResponse(attrIDs, attrValues, ssTransID);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import javax.bluetooth.DataElement;
|
||||
import javax.bluetooth.UUID;
|
||||
|
||||
/*
|
||||
* Provides ServiceSearchAttribute transaction functionality.
|
||||
*/
|
||||
public class ClientServiceSearchAttributeTransaction extends SDPClientTransaction {
|
||||
|
||||
/* ServiceSearchPattern (BT Spec 1.2 Vol 3 page 142). */
|
||||
DataElement uuidData;
|
||||
/* AttributeIDList (BT Spec 1.2 Vol 3 page 142). */
|
||||
DataElement attrData;
|
||||
/* AttributeLists (BT Spec 1.2 Vol 3 page 143). */
|
||||
byte[] attributes;
|
||||
|
||||
/*
|
||||
* Constructs ServiceSearchAttributeTransaction object.
|
||||
*/
|
||||
public ClientServiceSearchAttributeTransaction(JavaSDPClient client, int transactionID,
|
||||
SDPResponseListener listener, int[] attrSet,
|
||||
UUID[] uuidSet) {
|
||||
super(client, SDPClientTransaction.SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST, transactionID,
|
||||
listener);
|
||||
attrData = new DataElement(DataElement.DATSEQ);
|
||||
uuidData = new DataElement(DataElement.DATSEQ);
|
||||
for (int i = 0; i < attrSet.length; i++) {
|
||||
attrData.addElement(new DataElement(DataElement.U_INT_2,
|
||||
attrSet[i]));
|
||||
}
|
||||
for (int i = 0; i < uuidSet.length; i++) {
|
||||
uuidData.addElement(new DataElement(DataElement.UUID,
|
||||
uuidSet[i]));
|
||||
}
|
||||
parameterLength = super.client.getConnection().getReaderWriter().getDataSize(attrData) +
|
||||
super.client.getConnection().getReaderWriter().getDataSize(uuidData) + 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes transaction-specific parameters into the PDU.
|
||||
*
|
||||
* @throws IOException
|
||||
* when an I/O error occurs
|
||||
*/
|
||||
void writeParameters() throws IOException {
|
||||
super.client.getConnection().getReaderWriter().writeDataElement(uuidData);
|
||||
super.client.getConnection().getReaderWriter().writeShort((short)MAX_ATTRIBUTE_BYTE_COUNT);
|
||||
super.client.getConnection().getReaderWriter().writeDataElement(attrData);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads transaction-specific parameters from the PDU.
|
||||
*
|
||||
* @param length
|
||||
* length of PDU's parameters
|
||||
* @throws IOException
|
||||
* when an I/O error occurs
|
||||
*/
|
||||
void readParameters(int length) throws IOException {
|
||||
short byteCount = super.client.getConnection().getReaderWriter().readShort();
|
||||
byte[] data = super.client.getConnection().getReaderWriter().readBytes(byteCount);
|
||||
if (attributes == null) {
|
||||
attributes = data;
|
||||
} else {
|
||||
byte[] temp = attributes;
|
||||
attributes = new byte[temp.length + byteCount];
|
||||
System.arraycopy(temp, 0, attributes, 0, temp.length);
|
||||
System.arraycopy(data, 0, attributes, temp.length,
|
||||
byteCount);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Completes the transaction by calling corresponding listener's method with the
|
||||
* data retrieved.
|
||||
*/
|
||||
public void complete() {
|
||||
DataElement attrList;
|
||||
DataElementSerializer des = new DataElementSerializer();
|
||||
try {
|
||||
Enumeration attributeLists = (Enumeration)des.
|
||||
restore(attributes).getValue();
|
||||
if (attributeLists.hasMoreElements()) {
|
||||
attrList = (DataElement)attributeLists.nextElement();
|
||||
} else {
|
||||
listener.serviceSearchAttributeResponse(null, null,
|
||||
ssTransID);
|
||||
return;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
listener.serviceSearchAttributeResponse(null, null,
|
||||
ssTransID);
|
||||
return;
|
||||
}
|
||||
int size = attrList.getSize() / 2;
|
||||
if (size == 0) {
|
||||
listener.serviceSearchAttributeResponse(null, null,
|
||||
ssTransID);
|
||||
return;
|
||||
}
|
||||
Enumeration elements = (Enumeration)attrList.getValue();
|
||||
int[] attrIDs = new int[size];
|
||||
DataElement[] attrValues = new DataElement[size];
|
||||
for (int i = 0; elements.hasMoreElements(); i++) {
|
||||
attrIDs[i] = (int)((DataElement)
|
||||
elements.nextElement()).getLong();
|
||||
attrValues[i] = ((DataElement)
|
||||
elements.nextElement());
|
||||
}
|
||||
listener.serviceSearchAttributeResponse(attrIDs, attrValues,
|
||||
ssTransID);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.bluetooth.DataElement;
|
||||
import javax.bluetooth.UUID;
|
||||
|
||||
/*
|
||||
* Provides ServiceSearch transaction functionality.
|
||||
*/
|
||||
public class ClientServiceSearchTransaction extends SDPClientTransaction {
|
||||
|
||||
/* ServiceSearchPattern (BT Spec 1.2 Vol 3 page 135). */
|
||||
DataElement serviceSearchPattern;
|
||||
/* Acquired service record handles. */
|
||||
int[] handleList;
|
||||
/* Current position in the handleList. */
|
||||
int offset;
|
||||
|
||||
/*
|
||||
* Constructs ServiceSearchTransaction object.
|
||||
*/
|
||||
public ClientServiceSearchTransaction(JavaSDPClient client, int transactionID,
|
||||
SDPResponseListener listener, UUID[] uuidSet) {
|
||||
super(client, SDPClientTransaction.SDP_SERVICE_SEARCH_REQUEST, transactionID, listener);
|
||||
serviceSearchPattern = new DataElement(DataElement.DATSEQ);
|
||||
for (int i = 0; i < uuidSet.length; i++) {
|
||||
serviceSearchPattern.addElement(new DataElement(
|
||||
DataElement.UUID, uuidSet[i]));
|
||||
}
|
||||
parameterLength = super.des.getDataSize(serviceSearchPattern) + 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes transaction-specific parameters into the PDU.
|
||||
*
|
||||
* @throws IOException when an I/O error occurs
|
||||
*/
|
||||
void writeParameters() throws IOException {
|
||||
super.client.getConnection().getReaderWriter().writeDataElement(serviceSearchPattern);
|
||||
super.client.getConnection().getReaderWriter().writeShort((short)MAX_SERVICE_RECORD_COUNT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads transaction-specific parameters from the PDU.
|
||||
*
|
||||
* @param length length of PDU's parameters
|
||||
* @throws IOException when an I/O error occurs
|
||||
*/
|
||||
void readParameters(int length) throws IOException {
|
||||
int totalServiceRecordCount = super.client.getConnection().getReaderWriter().readShort();
|
||||
int currentServiceRecordCount = super.client.getConnection().getReaderWriter().readShort();
|
||||
if (handleList == null && totalServiceRecordCount > 0) {
|
||||
handleList = new int[totalServiceRecordCount];
|
||||
}
|
||||
for (int i = 0; i < currentServiceRecordCount; i++) {
|
||||
handleList[offset] = super.client.getConnection().getReaderWriter().readInteger();
|
||||
offset++;
|
||||
}
|
||||
System.out.println("<<< " + totalServiceRecordCount + " ServiceRecords found" );
|
||||
}
|
||||
|
||||
/*
|
||||
* Completes the transaction by calling corresponding listener's
|
||||
* method with the data retrieved.
|
||||
*/
|
||||
public void complete() {
|
||||
listener.serviceSearchResponse(handleList, ssTransID);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,629 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Stack;
|
||||
import javax.bluetooth.DataElement;
|
||||
import javax.bluetooth.ServiceRecord;
|
||||
import javax.bluetooth.UUID;
|
||||
|
||||
/*
|
||||
* Serializes and restores DataElement objects.
|
||||
*/
|
||||
public class DataElementSerializer {
|
||||
/* NULL data header. */
|
||||
private static final byte NULL_DATA = 0x00;
|
||||
|
||||
/* Boolean data header. */
|
||||
private static final byte BOOLEAN_DATA = 0x28;
|
||||
|
||||
/* 1-byte signed integer header. */
|
||||
private static final byte INT1_SIGNED = 0x10;
|
||||
|
||||
/* 2-byte signed integer header. */
|
||||
private static final byte INT2_SIGNED = 0x11;
|
||||
|
||||
/* 4-byte signed integer header. */
|
||||
private static final byte INT4_SIGNED = 0x12;
|
||||
|
||||
/* 8-byte signed integer header. */
|
||||
private static final byte INT8_SIGNED = 0x13;
|
||||
|
||||
/* 16-byte signed integer header. */
|
||||
private static final byte INT16_SIGNED = 0x14;
|
||||
|
||||
/* 1-byte unsigned integer header. */
|
||||
private static final byte INT1_UNSIGNED = 0x08;
|
||||
|
||||
/* 2-byte unsigned integer header. */
|
||||
private static final byte INT2_UNSIGNED = 0x09;
|
||||
|
||||
/* 4-byte unsigned integer header. */
|
||||
private static final byte INT4_UNSIGNED = 0x0a;
|
||||
|
||||
/* 8-byte unsigned integer header. */
|
||||
private static final byte INT8_UNSIGNED = 0x0b;
|
||||
|
||||
/* 16-byte unsigned integer header. */
|
||||
private static final byte INT16_UNSIGNED = 0x0c;
|
||||
|
||||
/* 16-bit UUID header. */
|
||||
private static final byte UUID_2 = 0x19;
|
||||
|
||||
/* 32-bit UUID header. */
|
||||
private static final byte UUID_4 = 0x1a;
|
||||
|
||||
/* 128-bit UUID header. */
|
||||
private static final byte UUID_16 = 0x1c;
|
||||
|
||||
/* Mask to get type tag from header. */
|
||||
private static final byte TYPE_MASK = ((byte)0xf8);
|
||||
|
||||
/* Mask to get size of data size field from header. */
|
||||
private static final byte SIZE_MASK = 0x07;
|
||||
|
||||
/* Tag for string type. */
|
||||
private static final byte STRING_TYPE = 0x20;
|
||||
|
||||
/* Tag for URL type. */
|
||||
private static final byte URL_TYPE = 0x40;
|
||||
|
||||
/* Tag for sequence type. */
|
||||
private static final byte SEQUENCE_TYPE = 0x30;
|
||||
|
||||
/* Tag for an alternative type. */
|
||||
private static final byte ALTERNATIVE_TYPE = 0x38;
|
||||
|
||||
/* Tag that identifies that size of data size field is 2 bytes. */
|
||||
private static final byte SHORT_SIZE = 0x05;
|
||||
|
||||
/* Tag that identifies that size of data size field is 4 bytes. */
|
||||
private static final byte NORMAL_SIZE = 0x06;
|
||||
|
||||
/* Tag that identifies that size of data size field is 8 bytes. */
|
||||
private static final byte LONG_SIZE = 0x07;
|
||||
|
||||
/* Destination buffer which collects binary data of a data element. */
|
||||
protected byte[] writeBuffer = null;
|
||||
|
||||
/* Source buffer which contains binary data of a data element. */
|
||||
protected byte[] readBuffer = null;
|
||||
|
||||
/* Current position at the destination buffer. */
|
||||
protected long writePos = 0;
|
||||
|
||||
/* Current position at the source buffer. */
|
||||
protected long readPos = 0;
|
||||
|
||||
/* Allows to store and retrieve positions at the source buffer. */
|
||||
private Stack readPosStack = new Stack();
|
||||
|
||||
/*
|
||||
* Constructs the serializer object.
|
||||
*/
|
||||
public DataElementSerializer() {
|
||||
}
|
||||
|
||||
/*
|
||||
* Serializes given DataElement object, i.e. creates an array of bytes
|
||||
* representing DataElement as described in Bluetooth Specification
|
||||
* Version 1.2, vol 3, page 127.
|
||||
*
|
||||
* @param data the data element to serialize
|
||||
* @return an array containing the serialized data element
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public synchronized byte[] serialize(DataElement data) throws IOException {
|
||||
writeBuffer = new byte[(int)getDataSize(data)];
|
||||
writePos = 0;
|
||||
writeDataElement(data);
|
||||
byte[] result = writeBuffer;
|
||||
writeBuffer = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructs DataElement from byte array containing the element in
|
||||
* serialized form.
|
||||
*
|
||||
* @param data byte array containing the element in serialized form
|
||||
* @return DataElement constructed from the binary data
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public synchronized DataElement restore(byte[] data) throws IOException {
|
||||
readBuffer = data;
|
||||
readPos = 0;
|
||||
DataElement result = readDataElement();
|
||||
readBuffer = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the size of DataElement with service information
|
||||
* to get the total size required to work with this DataElement.
|
||||
*
|
||||
* @param data the data element to get packet size for
|
||||
* @return number of bytes needed to store given data element
|
||||
*/
|
||||
public long getDataSize(DataElement data) {
|
||||
int type = data.getDataType();
|
||||
long size = getPureDataSize(data);
|
||||
if ((type == DataElement.NULL) || (type == DataElement.BOOL)
|
||||
|| (type == DataElement.INT_1) || (type == DataElement.U_INT_1)
|
||||
|| (type == DataElement.INT_2) || (type == DataElement.U_INT_2)
|
||||
|| (type == DataElement.INT_4) || (type == DataElement.U_INT_4)
|
||||
|| (type == DataElement.INT_8) || (type == DataElement.U_INT_8)
|
||||
|| (type == DataElement.INT_16)
|
||||
|| (type == DataElement.U_INT_16)
|
||||
|| (type == DataElement.UUID)) {
|
||||
return size + 1;
|
||||
} else if ((type == DataElement.DATSEQ)
|
||||
|| (type == DataElement.DATALT) || (type == DataElement.STRING)
|
||||
|| (type == DataElement.URL)) {
|
||||
if (size <= 0xffL) {
|
||||
return size + 2;
|
||||
} else if (size <= 0xffffL) {
|
||||
return size + 3;
|
||||
} else if (size <= 0xffffffffL) {
|
||||
return size + 5;
|
||||
} else {
|
||||
throw new RuntimeException("Data size is too large.");
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected data type.");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the size of DataElement without service information.
|
||||
*
|
||||
* @param data the data element to get pure data size for
|
||||
* @return pure data size in bytes
|
||||
*/
|
||||
public long getPureDataSize(DataElement data) {
|
||||
switch (data.getDataType()) {
|
||||
case DataElement.NULL:
|
||||
return 0;
|
||||
case DataElement.BOOL:
|
||||
case DataElement.INT_1:
|
||||
case DataElement.U_INT_1:
|
||||
return 1;
|
||||
case DataElement.INT_2:
|
||||
case DataElement.U_INT_2:
|
||||
return 2;
|
||||
case DataElement.INT_4:
|
||||
case DataElement.U_INT_4:
|
||||
return 4;
|
||||
case DataElement.INT_8:
|
||||
case DataElement.U_INT_8:
|
||||
return 8;
|
||||
case DataElement.INT_16:
|
||||
case DataElement.U_INT_16:
|
||||
return 16;
|
||||
case DataElement.DATSEQ:
|
||||
case DataElement.DATALT:
|
||||
long size = 0;
|
||||
Enumeration elements = (Enumeration)data.getValue();
|
||||
while (elements.hasMoreElements()) {
|
||||
size += getDataSize((DataElement)elements.nextElement());
|
||||
}
|
||||
return size;
|
||||
case DataElement.STRING:
|
||||
case DataElement.URL:
|
||||
return ((String)data.getValue()).length();
|
||||
case DataElement.UUID:
|
||||
return 16;
|
||||
default:
|
||||
throw new RuntimeException("Unknown data type.");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes given data element into the write buffer.
|
||||
*
|
||||
* @param data the data element to write
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void writeDataElement(DataElement data) throws IOException {
|
||||
long size = getPureDataSize(data);
|
||||
int type = data.getDataType();
|
||||
byte typeBits = 0x00;
|
||||
if ((type == DataElement.NULL) || (type == DataElement.BOOL)
|
||||
|| (type == DataElement.INT_1) || (type == DataElement.U_INT_1)
|
||||
|| (type == DataElement.INT_2) || (type == DataElement.U_INT_2)
|
||||
|| (type == DataElement.INT_4) || (type == DataElement.U_INT_4)
|
||||
|| (type == DataElement.INT_8) || (type == DataElement.U_INT_8)
|
||||
|| (type == DataElement.INT_16)
|
||||
|| (type == DataElement.U_INT_16)) {
|
||||
switch (type) {
|
||||
case DataElement.NULL:
|
||||
writeByte(NULL_DATA);
|
||||
break;
|
||||
case DataElement.BOOL:
|
||||
writeByte(BOOLEAN_DATA);
|
||||
writeBoolean(data.getBoolean());
|
||||
break;
|
||||
case DataElement.INT_1:
|
||||
writeByte(INT1_SIGNED);
|
||||
writeByte((byte)data.getLong());
|
||||
break;
|
||||
case DataElement.U_INT_1:
|
||||
writeByte(INT1_UNSIGNED);
|
||||
writeByte((byte)data.getLong());
|
||||
break;
|
||||
case DataElement.INT_2:
|
||||
writeByte(INT2_SIGNED);
|
||||
writeShort((short)data.getLong());
|
||||
break;
|
||||
case DataElement.U_INT_2:
|
||||
writeByte(INT2_UNSIGNED);
|
||||
writeShort((short)data.getLong());
|
||||
break;
|
||||
case DataElement.INT_4:
|
||||
writeByte(INT4_SIGNED);
|
||||
writeInteger((int)data.getLong());
|
||||
break;
|
||||
case DataElement.U_INT_4:
|
||||
writeByte(INT4_UNSIGNED);
|
||||
writeInteger((int)data.getLong());
|
||||
break;
|
||||
case DataElement.INT_8:
|
||||
writeByte(INT8_SIGNED);
|
||||
writeLong(data.getLong());
|
||||
break;
|
||||
case DataElement.U_INT_8:
|
||||
writeByte(INT8_UNSIGNED);
|
||||
writeBytes((byte[])data.getValue());
|
||||
break;
|
||||
case DataElement.INT_16:
|
||||
writeByte(INT16_SIGNED);
|
||||
writeBytes((byte[])data.getValue());
|
||||
break;
|
||||
case DataElement.U_INT_16:
|
||||
writeByte(INT16_UNSIGNED);
|
||||
writeBytes((byte[])data.getValue());
|
||||
break;
|
||||
}
|
||||
} else if ((type == DataElement.DATSEQ)
|
||||
|| (type == DataElement.DATALT) || (type == DataElement.STRING)
|
||||
|| (type == DataElement.URL)) {
|
||||
switch (type) {
|
||||
case DataElement.DATSEQ:
|
||||
typeBits = (TYPE_MASK & SEQUENCE_TYPE);
|
||||
break;
|
||||
case DataElement.DATALT:
|
||||
typeBits = (TYPE_MASK & ALTERNATIVE_TYPE);
|
||||
break;
|
||||
case DataElement.STRING:
|
||||
typeBits = (TYPE_MASK & STRING_TYPE);
|
||||
break;
|
||||
case DataElement.URL:
|
||||
typeBits = (TYPE_MASK & URL_TYPE);
|
||||
break;
|
||||
}
|
||||
if (size <= 0xff) {
|
||||
writeByte(typeBits | (SIZE_MASK & SHORT_SIZE));
|
||||
writeByte((byte)size);
|
||||
} else if (size <= 0xffff) {
|
||||
writeByte(typeBits | (SIZE_MASK & NORMAL_SIZE));
|
||||
writeShort((short)size);
|
||||
} else {
|
||||
writeByte(typeBits | (SIZE_MASK & LONG_SIZE));
|
||||
writeInteger((int)size);
|
||||
}
|
||||
if ((type == DataElement.DATSEQ) || (type == DataElement.DATALT)) {
|
||||
Enumeration elements = (Enumeration) data.getValue();
|
||||
while (elements.hasMoreElements()) {
|
||||
writeDataElement((DataElement)elements.nextElement());
|
||||
}
|
||||
} else {
|
||||
writeBytes(((String)data.getValue()).getBytes());
|
||||
}
|
||||
} else if (type == DataElement.UUID) {
|
||||
writeByte(UUID_16);
|
||||
String uuid = ((UUID)data.getValue()).toString();
|
||||
while (uuid.length() < 32) {
|
||||
uuid = '0' + uuid;
|
||||
}
|
||||
for (int i = 0; i < 16; i++) {
|
||||
writeByte(Integer.parseInt(
|
||||
uuid.substring(i * 2, i * 2 + 2), 16));
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Unknown data type.");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a data element from the binary data in the read buffer.
|
||||
*
|
||||
* @return <code>DataElement</code> read
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public DataElement readDataElement() throws IOException {
|
||||
byte header = readByte();
|
||||
if ((header == NULL_DATA) || (header == BOOLEAN_DATA)
|
||||
|| (header == INT1_SIGNED) || (header == INT1_UNSIGNED)
|
||||
|| (header == INT2_SIGNED) || (header == INT2_UNSIGNED)
|
||||
|| (header == INT4_SIGNED) || (header == INT4_UNSIGNED)
|
||||
|| (header == INT8_SIGNED) || (header == INT8_UNSIGNED)
|
||||
|| (header == INT16_SIGNED) || (header == INT16_UNSIGNED)) {
|
||||
switch (header) {
|
||||
case NULL_DATA:
|
||||
return new DataElement(DataElement.NULL);
|
||||
case BOOLEAN_DATA:
|
||||
return new DataElement(readBoolean());
|
||||
case INT1_SIGNED:
|
||||
return new DataElement(DataElement.INT_1, readByte());
|
||||
case INT1_UNSIGNED:
|
||||
return new DataElement(DataElement.U_INT_1,
|
||||
(readByte() & 0xffL));
|
||||
case INT2_SIGNED:
|
||||
return new DataElement(DataElement.INT_2, readShort());
|
||||
case INT2_UNSIGNED:
|
||||
return new DataElement(DataElement.U_INT_2,
|
||||
(readShort() & 0xffffL));
|
||||
case INT4_SIGNED:
|
||||
return new DataElement(DataElement.INT_4, readInteger());
|
||||
case INT4_UNSIGNED:
|
||||
return new DataElement(DataElement.U_INT_4,
|
||||
(readInteger() & 0xffffffffL));
|
||||
case INT8_SIGNED:
|
||||
return new DataElement(DataElement.INT_8, readLong());
|
||||
case INT8_UNSIGNED:
|
||||
return new DataElement(DataElement.U_INT_8, readBytes(8));
|
||||
case INT16_SIGNED:
|
||||
return new DataElement(DataElement.INT_16, readBytes(16));
|
||||
case INT16_UNSIGNED:
|
||||
return new DataElement(DataElement.U_INT_16, readBytes(16));
|
||||
}
|
||||
} else if (((header & TYPE_MASK) == STRING_TYPE)
|
||||
|| ((header & TYPE_MASK) == URL_TYPE)
|
||||
|| ((header & TYPE_MASK) == SEQUENCE_TYPE)
|
||||
|| ((header & TYPE_MASK) == ALTERNATIVE_TYPE)) {
|
||||
long size = 0;
|
||||
if ((header & SIZE_MASK) == SHORT_SIZE) {
|
||||
size = readByte() & 0xffL;
|
||||
} else if ((header & SIZE_MASK) == NORMAL_SIZE) {
|
||||
size = readShort() & 0xffffL;
|
||||
} else if ((header & SIZE_MASK) == LONG_SIZE) {
|
||||
size = readInteger() & 0xffffffffL;
|
||||
} else {
|
||||
System.err.println("Unknown size mask.");
|
||||
}
|
||||
if ((header & TYPE_MASK) == STRING_TYPE) {
|
||||
return new DataElement(DataElement.STRING,
|
||||
new String(readBytes((int)size)));
|
||||
} else if ((header & TYPE_MASK) == URL_TYPE) {
|
||||
return new DataElement(DataElement.URL,
|
||||
new String(readBytes((int)size)));
|
||||
} else {
|
||||
DataElement data = null;
|
||||
DataElement dataElement = null;
|
||||
long dataPos = 0;
|
||||
if ((header & TYPE_MASK) == SEQUENCE_TYPE) {
|
||||
data = new DataElement(DataElement.DATSEQ);
|
||||
} else {
|
||||
data = new DataElement(DataElement.DATALT);
|
||||
}
|
||||
while (dataPos < size) {
|
||||
pushReadPos();
|
||||
dataElement = readDataElement();
|
||||
dataPos += readPos - popReadPos();
|
||||
data.addElement(dataElement);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
} else if (header == UUID_2) {
|
||||
return new DataElement(DataElement.UUID, readUUID(2));
|
||||
} else if (header == UUID_4) {
|
||||
return new DataElement(DataElement.UUID, readUUID(4));
|
||||
} else if (header == UUID_16) {
|
||||
return new DataElement(DataElement.UUID, readUUID(16));
|
||||
} else {
|
||||
throw new RuntimeException("Unknown data type.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes boolean data to the buffer.
|
||||
* Writes only value given itself. Note that boolean data header
|
||||
* should be written before.
|
||||
*
|
||||
* @param data boolean value to write.
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void writeBoolean(boolean data) throws IOException {
|
||||
writeByte(data ? 1 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes 1-byte data to the buffer.
|
||||
*
|
||||
* @param data byte value to write.
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void writeByte(long data) throws IOException {
|
||||
if (writePos < 0 || writePos >= writeBuffer.length) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
writeBuffer[(int)writePos++] = (byte)data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes 2-byte data to the buffer.
|
||||
*
|
||||
* @param data 2-byte value to write.
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void writeShort(short data) throws IOException {
|
||||
writeByte((byte)((data >>> 8) & 0xff));
|
||||
writeByte((byte)((data >>> 0) & 0xff));
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes 4-byte data to the connection.
|
||||
*
|
||||
* @param data 4-byte value to write.
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void writeInteger(int data) throws IOException {
|
||||
writeShort((short)((data >>> 16) & 0xffff));
|
||||
writeShort((short)((data >>> 0) & 0xffff));
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes 8-byte data to the connection.
|
||||
*
|
||||
* @param data 8-byte value to write.
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void writeLong(long data) throws IOException {
|
||||
writeInteger((int)((data >>> 32) & 0xffffffff));
|
||||
writeInteger((int)((data >>> 0) & 0xffffffff));
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes given data to the connection.
|
||||
*
|
||||
* @param data bytes to write.
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void writeBytes(byte[] data) throws IOException {
|
||||
if (writePos < 0 || writePos + data.length > writeBuffer.length) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
System.arraycopy(data, 0, writeBuffer, (int)writePos, data.length);
|
||||
writePos += data.length;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads boolean value from the connection.
|
||||
*
|
||||
* @return boolean value recieved.
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public boolean readBoolean() throws IOException {
|
||||
return (readByte() != 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads 1-byte value from the connection.
|
||||
*
|
||||
* @return byte recieved.
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public byte readByte() throws IOException {
|
||||
if (readPos < 0 || readPos >= readBuffer.length) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
return readBuffer[(int)readPos++];
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads 2-byte value from the connection.
|
||||
*
|
||||
* @return short which is the 2 bytes read.
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public short readShort() throws IOException {
|
||||
int data1 = ((int)readByte()) & 0xff;
|
||||
int data2 = ((int)readByte()) & 0xff;
|
||||
return (short)((data1 << 8) + (data2 << 0));
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads 4-byte value from the connection.
|
||||
*
|
||||
* @return int which is the 4 bytes read.
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public int readInteger() throws IOException {
|
||||
int data1 = ((int)readShort()) & 0xffff;
|
||||
int data2 = ((int)readShort()) & 0xffff;
|
||||
return ((data1 << 16) + (data2 << 0));
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads 8-byte value from the connection.
|
||||
*
|
||||
* @return long which is the 8 bytes read.
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public long readLong() throws IOException {
|
||||
long data1 = ((long)readInteger()) & 0xffffffffL;
|
||||
long data2 = ((long)readInteger()) & 0xffffffffL;
|
||||
return ((data1 << 32) + (data2 << 0));
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads given number of bytes from the connection.
|
||||
*
|
||||
* @param size number of bytes to read.
|
||||
* @return array of bytes read.
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public byte[] readBytes(int size) throws IOException {
|
||||
byte[] data = new byte[size];
|
||||
int dataPos = 0;
|
||||
if (readPos < 0 || readPos + data.length > readBuffer.length) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
System.arraycopy(readBuffer, (int)readPos, data, 0, data.length);
|
||||
readPos += data.length;
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads UUID of a given size.
|
||||
*
|
||||
* @param len number of bytes to read
|
||||
* @return UUID created from <code>len</code> bytes
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public UUID readUUID(int len) throws IOException {
|
||||
String uuid = "";
|
||||
for (int i = 0; i < len; i++) {
|
||||
String digit = Integer.toHexString(readByte() & 0xff);
|
||||
if (digit.length() == 1) digit = '0' + digit;
|
||||
uuid += digit;
|
||||
}
|
||||
return new UUID(uuid, len < 16);
|
||||
}
|
||||
|
||||
/* Saves the current read position. */
|
||||
private void pushReadPos() {
|
||||
readPosStack.push(new Long(readPos));
|
||||
}
|
||||
|
||||
/* Extracts saved read position. */
|
||||
private long popReadPos() {
|
||||
return ((Long)readPosStack.pop()).longValue();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Stack;
|
||||
import javax.bluetooth.DataElement;
|
||||
import javax.bluetooth.L2CAPConnection;
|
||||
import javax.bluetooth.UUID;
|
||||
|
||||
/*
|
||||
* Logical Link Control and Adaptation Protocol connection reader/writer.
|
||||
*/
|
||||
public class DataL2CAPReaderWriter extends DataElementSerializer {
|
||||
|
||||
/* The L2CAP connection to read from or write to. */
|
||||
private L2CAPConnection con = null;
|
||||
|
||||
/* Actual size of the read buffer. */
|
||||
private long readSize = 0;
|
||||
|
||||
/*
|
||||
* Constructs reader-and-writer object for the given connection.
|
||||
*
|
||||
* @param con the L2CAP connection to create reader-writer for.
|
||||
*/
|
||||
public DataL2CAPReaderWriter(L2CAPConnection con) throws IOException {
|
||||
this.con = con;
|
||||
writeBuffer = new byte[con.getTransmitMTU()];
|
||||
readBuffer = new byte[con.getReceiveMTU()];
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes 1-byte data to the connection.
|
||||
*
|
||||
* @param data byte value to write.
|
||||
*/
|
||||
public void writeByte(long data) throws IOException {
|
||||
if ((writePos < 0) || (writePos >= writeBuffer.length)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
writeBuffer[(int)writePos] = (byte)data;
|
||||
writePos++;
|
||||
if (writePos == writeBuffer.length) {
|
||||
con.send(writeBuffer);
|
||||
writePos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes given data to the connection.
|
||||
*
|
||||
* @param data bytes to write.
|
||||
*/
|
||||
public void writeBytes(byte[] data) throws IOException {
|
||||
if ((writePos < 0) || (writePos >= writeBuffer.length)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
int dataPos = 0;
|
||||
|
||||
while ((data.length - dataPos) >= ((writeBuffer.length - writePos))) {
|
||||
int length = writeBuffer.length - ((int) writePos);
|
||||
System.arraycopy(data, (int) dataPos, writeBuffer, (int) writePos,
|
||||
(int) length);
|
||||
con.send(writeBuffer);
|
||||
writePos = 0;
|
||||
dataPos += length;
|
||||
}
|
||||
int length = data.length - dataPos;
|
||||
|
||||
if (length > 0) {
|
||||
System.arraycopy(data, (int) dataPos, writeBuffer, (int) writePos,
|
||||
(int) length);
|
||||
}
|
||||
writePos += length;
|
||||
}
|
||||
|
||||
/*
|
||||
* Flushes write buffer to the conection.
|
||||
*/
|
||||
public void flush() throws IOException {
|
||||
if ((writePos < 0) || (writePos >= writeBuffer.length)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
if (writePos == 0) {
|
||||
return;
|
||||
}
|
||||
byte[] tempBuffer = new byte[(int)writePos];
|
||||
System.arraycopy(writeBuffer, 0, tempBuffer, 0, (int)writePos);
|
||||
con.send(tempBuffer);
|
||||
writePos = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads 1-byte value from the connection.
|
||||
*
|
||||
* @return byte recieved.
|
||||
*/
|
||||
public byte readByte() throws IOException {
|
||||
if ((readPos == 0) || (readPos == readSize)) {
|
||||
if ((readSize = con.receive(readBuffer)) == 0) {
|
||||
throw new IOException("Empty packet is received");
|
||||
}
|
||||
readPos = 0;
|
||||
} else if ((readPos < 0) || (readPos > readSize)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
if (readSize == -1) {
|
||||
throw new IOException("Reached end of stream");
|
||||
}
|
||||
|
||||
byte data = readBuffer[(int)readPos];
|
||||
readPos++;
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads given number of bytes from the connection.
|
||||
*
|
||||
* @param size number of bytes to read.
|
||||
* @return array of bytes read.
|
||||
*/
|
||||
public byte[] readBytes(int size) throws IOException {
|
||||
byte[] data = new byte[size];
|
||||
int dataPos = 0;
|
||||
|
||||
if ((readPos == 0) || (readPos == readSize)) {
|
||||
readSize = con.receive(readBuffer);
|
||||
readPos = 0;
|
||||
} else if ((readPos < 0) || (readPos > readSize)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
while ((data.length - dataPos) > (readSize - readPos)) {
|
||||
int length = (int) (readSize - readPos);
|
||||
System.arraycopy(readBuffer, (int)readPos, data, (int)dataPos,
|
||||
length);
|
||||
dataPos += length;
|
||||
readSize = con.receive(readBuffer);
|
||||
readPos = 0;
|
||||
}
|
||||
int length = data.length - dataPos;
|
||||
System.arraycopy(readBuffer, (int)readPos, data, (int)dataPos,
|
||||
length);
|
||||
readPos += length;
|
||||
return data;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import javax.bluetooth.BluetoothStateException;
|
||||
import javax.bluetooth.DiscoveryAgent;
|
||||
import javax.bluetooth.DiscoveryListener;
|
||||
import javax.bluetooth.RemoteDevice;
|
||||
import javax.bluetooth.UUID;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
|
||||
/*
|
||||
* The <code>DiscoveryAgentImpl</code> class is a DiscoveryAgent
|
||||
* API class implementation which does not extend this API class.
|
||||
*/
|
||||
public final class DiscoveryAgentImpl {
|
||||
/* Calls inquiry completion callback in a separate thread. */
|
||||
class Completed implements Runnable {
|
||||
/* type of completion. */
|
||||
private int discType;
|
||||
/* listener to be called. */
|
||||
private DiscoveryListener listener;
|
||||
/* Constructs an instance and starts the the thread. */
|
||||
Completed(DiscoveryListener listener, int discType) {
|
||||
this.listener = listener;
|
||||
this.discType = discType;
|
||||
new Thread(this).start();
|
||||
}
|
||||
/* Implements Runnable. */
|
||||
public void run() {
|
||||
if (listener != null) {
|
||||
listener.inquiryCompleted(discType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set to false in RR version - then the javac skip the code. */
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/*
|
||||
* maximum number of allowed UUIDS in search uuids sequence
|
||||
*/
|
||||
private static final int MAX_ALLOWED_UUIDS = 12;
|
||||
|
||||
/*
|
||||
* Keeps an instance to the object of this class to be
|
||||
* accessible from the implementation.
|
||||
*/
|
||||
private static DiscoveryAgentImpl instance;
|
||||
|
||||
/* Keeps the <code>RemoteDeviceImpl</code> references of known devices. */
|
||||
private Hashtable knownDevices = new Hashtable();
|
||||
|
||||
/* Keeps the <code>RemoteDeviceImpl</code> references of cached devices. */
|
||||
private Hashtable cachedDevices = new Hashtable();
|
||||
|
||||
/*
|
||||
* Keeps the listener of the device discovery inquire.
|
||||
* Also, it is used as flag that a device is in inquire mode.
|
||||
*/
|
||||
private DiscoveryListener d_listener;
|
||||
|
||||
/* Keeps the lock object for device discovery synchronization. */
|
||||
private Object d_lock = new Object();
|
||||
|
||||
/* Keeps the reference to module responsible for selecting services. */
|
||||
private SelectServiceHandler selectServiceHandler =
|
||||
new SelectServiceHandler(this);
|
||||
|
||||
/* Constructs the single instance. */
|
||||
private DiscoveryAgentImpl() {}
|
||||
|
||||
public RemoteDevice[] retrieveDevices(int option) {
|
||||
switch (option) {
|
||||
case DiscoveryAgent.CACHED:
|
||||
// IMPL_NOTE: use native cache keeping addresses of found devices
|
||||
// to share the cache between multiple isolates
|
||||
return getCachedDevices();
|
||||
case DiscoveryAgent.PREKNOWN:
|
||||
Vector pk = BCC.getInstance().getPreknownDevices();
|
||||
if (pk == null || pk.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
RemoteDevice[] res = new RemoteDevice[pk.size()];
|
||||
for (int i = 0; i < pk.size(); i++) {
|
||||
String addr = (String)pk.elementAt(i);
|
||||
res[i] = getRemoteDevice(addr);
|
||||
}
|
||||
return res;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid option value: "
|
||||
+ option);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Adds address of remote device found during inquiry request to internal
|
||||
* inquiry cache.
|
||||
*
|
||||
* The method does nothing if the RemoteDevice is already in the cache.
|
||||
*/
|
||||
public void addCachedDevice(String addr) {
|
||||
RemoteDevice rd = getRemoteDevice(addr);
|
||||
synchronized (cachedDevices) {
|
||||
cachedDevices.put(addr, rd);
|
||||
}
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private RemoteDevice[] getCachedDevices() {
|
||||
synchronized (cachedDevices) {
|
||||
int len = cachedDevices.size();
|
||||
if (len == 0) {
|
||||
return null;
|
||||
}
|
||||
RemoteDevice[] res = new RemoteDevice[len];
|
||||
Enumeration e = cachedDevices.elements();
|
||||
for (int i = 0; e.hasMoreElements(); i++) {
|
||||
res[i] = (RemoteDevice)e.nextElement();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean startInquiry(int accessCode, DiscoveryListener listener)
|
||||
throws BluetoothStateException {
|
||||
|
||||
if (accessCode != DiscoveryAgent.GIAC &&
|
||||
accessCode != DiscoveryAgent.LIAC &&
|
||||
(accessCode < 0x9E8B00 || accessCode > 0x9E8B3F)) {
|
||||
throw new IllegalArgumentException("Access code is out of range: "
|
||||
+ accessCode);
|
||||
}
|
||||
|
||||
if (listener == null) {
|
||||
throw new NullPointerException("null listener");
|
||||
}
|
||||
|
||||
/* IMPL_NOTE see
|
||||
// kvem/classes/com/sun/kvem/jsr082/impl/bluetooth/
|
||||
// BTDeviceDiscoverer.java
|
||||
// heck what access codes should be supported.
|
||||
// Return false if access code is not supported.
|
||||
*/
|
||||
|
||||
synchronized (d_lock) {
|
||||
if (d_listener != null) {
|
||||
throw new BluetoothStateException(
|
||||
"The previous device discovery is running...");
|
||||
}
|
||||
d_listener = listener;
|
||||
|
||||
/* process the inquiry in the device specific way */
|
||||
return startInquiry(accessCode);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean startInquiry(int accessCode) throws BluetoothStateException {
|
||||
return BluetoothStack.getEnabledInstance().startInquiry(
|
||||
accessCode, d_listener);
|
||||
}
|
||||
|
||||
public boolean cancelInquiry(DiscoveryListener listener) {
|
||||
if (listener == null) {
|
||||
throw new NullPointerException("null listener");
|
||||
}
|
||||
synchronized (d_lock) {
|
||||
|
||||
/* no inquiry was started */
|
||||
if (d_listener == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* not valid listener */
|
||||
if (d_listener != listener) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* process the inquiry in the device specific way */
|
||||
cancelInquiry();
|
||||
}
|
||||
|
||||
inquiryCompleted(DiscoveryListener.INQUIRY_TERMINATED);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cancels inquiry in device specific way.
|
||||
*/
|
||||
private void cancelInquiry() {
|
||||
BluetoothStack.getInstance().cancelInquiry(d_listener);
|
||||
}
|
||||
|
||||
/*
|
||||
* Porting interface: this method is used by the device specific
|
||||
* implementation to notify this class, that the current inquire
|
||||
* has been completed.
|
||||
*
|
||||
* @param discType type of completion:
|
||||
* <code>DiscoveryListener.INQUIRY_COMPLETED</code>, or
|
||||
* <code>DiscoveryListener.INQUIRY_TERMINATED</code>, or
|
||||
* <code>DiscoveryListener.INQUIRY_ERROR</code>
|
||||
*/
|
||||
public void inquiryCompleted(int discType) {
|
||||
DiscoveryListener listener;
|
||||
synchronized (d_lock) {
|
||||
listener = d_listener;
|
||||
d_listener = null;
|
||||
}
|
||||
|
||||
new Completed(listener, discType);
|
||||
}
|
||||
|
||||
/*
|
||||
* Porting interface: this method is used by the device specific
|
||||
* implementation to create the RemoteDevice object by address.
|
||||
*
|
||||
* Also, this method puts the new remote devices into cache of
|
||||
* known devices.
|
||||
*
|
||||
* @param addr address of remote device to be created
|
||||
*
|
||||
* @return new <code>RemoteDeviceImpl</code>instance if device with address
|
||||
* given is unknown, the known one otherwise.
|
||||
*/
|
||||
public RemoteDeviceImpl getRemoteDevice(String addr) {
|
||||
synchronized (knownDevices) {
|
||||
addr = addr.toUpperCase();
|
||||
RemoteDeviceImpl rd = (RemoteDeviceImpl) knownDevices.get(addr);
|
||||
|
||||
if (rd == null) {
|
||||
rd = new RemoteDeviceImpl(addr);
|
||||
knownDevices.put(addr, rd);
|
||||
}
|
||||
return rd;
|
||||
}
|
||||
}
|
||||
|
||||
public int searchServices(int[] attrSet, UUID[] uuidSet, RemoteDevice btDev,
|
||||
DiscoveryListener discListener) throws BluetoothStateException {
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("searchServices: ");
|
||||
System.out.println("\tattrSet=" + attrSet);
|
||||
|
||||
if (attrSet != null) {
|
||||
for (int i = 0; i < attrSet.length; i++) {
|
||||
System.out.println("\tattrSet[" + i + "]=0x" + attrSet[i]);
|
||||
}
|
||||
}
|
||||
System.out.println("\tuuidSet=" + uuidSet);
|
||||
|
||||
if (uuidSet != null) {
|
||||
for (int i = 0; i < uuidSet.length; i++) {
|
||||
System.out.println("\tuuidSet[" + i + "]=" + uuidSet[i]);
|
||||
}
|
||||
}
|
||||
System.out.println("\tadderess=" + btDev.getBluetoothAddress());
|
||||
}
|
||||
if (uuidSet == null) {
|
||||
throw new NullPointerException("UUID set is null");
|
||||
}
|
||||
|
||||
if (uuidSet.length == 0 || uuidSet.length > MAX_ALLOWED_UUIDS ) {
|
||||
throw new IllegalArgumentException("Invalid UUID set length");
|
||||
}
|
||||
|
||||
if (btDev == null) {
|
||||
throw new NullPointerException("null instance of RemoteDevice");
|
||||
}
|
||||
|
||||
/* the 'transID' is assigned by service discoverer */
|
||||
int transID = ServiceDiscovererFactory.getServiceDiscoverer().
|
||||
searchService(
|
||||
ServiceSearcherBase.extendByStandardAttrs(attrSet),
|
||||
ServiceSearcherBase.removeDuplicatedUuids(uuidSet),
|
||||
btDev, discListener);
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("\ttransID=" + transID);
|
||||
}
|
||||
return transID;
|
||||
}
|
||||
|
||||
public boolean cancelServiceSearch(int transID) {
|
||||
if (DEBUG) {
|
||||
System.out.println("cancelServiceSearch: transID=" + transID);
|
||||
}
|
||||
return ServiceDiscovererFactory.getServiceDiscoverer().cancel(transID);
|
||||
}
|
||||
|
||||
public String selectService(UUID uuid, int security, boolean master)
|
||||
throws BluetoothStateException {
|
||||
|
||||
// use the separated class to light this one
|
||||
return selectServiceHandler.selectService(uuid, security, master);
|
||||
// return ServiceDiscovererFactory.getServiceDiscoverer().
|
||||
// selectService(uuid, security, master, this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the instance of this singleton constructing it if needed.
|
||||
* @return the only instance of <code>DiscoveryAgentImpl</code>.
|
||||
*/
|
||||
public static synchronized DiscoveryAgentImpl getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new DiscoveryAgentImpl();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* Encryption change event.
|
||||
*/
|
||||
public class EncryptionChangeEvent extends BluetoothEvent {
|
||||
|
||||
/* ACL connection handle for which encryption change has been performed. */
|
||||
private int handle;
|
||||
|
||||
/* Indicates whether the change has occured. */
|
||||
private boolean success;
|
||||
|
||||
/* Indicates whether the link encryption is enabled. */
|
||||
private boolean enabled;
|
||||
|
||||
/*
|
||||
* Class constructor.
|
||||
*
|
||||
* @param aclHandle ACL connection handle
|
||||
* @param succ true if the change occured, false otherwise
|
||||
* @param on true if the change occured, false otherwise
|
||||
*/
|
||||
public EncryptionChangeEvent(int aclHandle, boolean succ, boolean on) {
|
||||
handle = aclHandle;
|
||||
success = succ;
|
||||
enabled = on;
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes this event.
|
||||
*/
|
||||
public void process() {
|
||||
BluetoothStack.getInstance().onEncryptionChange(handle, success);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* Inquiry completion event.
|
||||
*/
|
||||
public class InquiryCompleteEvent extends BluetoothEvent {
|
||||
|
||||
/* Indicates whether the inquiry succeeded. */
|
||||
private boolean success;
|
||||
|
||||
/*
|
||||
* Class constructor.
|
||||
*
|
||||
* @param succ true if the inquiry succeeded, false otherwise
|
||||
*/
|
||||
public InquiryCompleteEvent(boolean succ) {
|
||||
super.eventName = "InquiryCompleteEvent";
|
||||
success = succ;
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes this event.
|
||||
*/
|
||||
public void process() {
|
||||
BluetoothStack.getInstance().onInquiryComplete(success);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
import javax.bluetooth.DeviceClass;
|
||||
|
||||
/*
|
||||
* Inquiry result record.
|
||||
*/
|
||||
class InquiryResult {
|
||||
|
||||
/* Bluetooth address of a discovered device. */
|
||||
private String address;
|
||||
|
||||
/* Class of a discovered device. */
|
||||
private DeviceClass deviceClass;
|
||||
|
||||
/*
|
||||
* Class constructor.
|
||||
*
|
||||
* @param addr Bluetooth address of a discovered device
|
||||
* @param cod class of a discovered device
|
||||
*/
|
||||
public InquiryResult(String addr, int cod) {
|
||||
address = addr;
|
||||
deviceClass = new DeviceClass(cod);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns Bluetooth address of the remote device.
|
||||
*
|
||||
* @return Bluetooth address of the remote device
|
||||
*/
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns class of the remote device.
|
||||
*
|
||||
* @return class of the device
|
||||
*/
|
||||
public DeviceClass getDeviceClass() {
|
||||
return deviceClass;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* Inquiry result event.
|
||||
*/
|
||||
public class InquiryResultEvent extends BluetoothEvent {
|
||||
|
||||
/* An array of inquiry results. */
|
||||
private InquiryResult[] results;
|
||||
|
||||
/*
|
||||
* Creates event using a single inquiry result record.
|
||||
*
|
||||
* @param result inquiry result record
|
||||
*/
|
||||
public InquiryResultEvent(InquiryResult result) {
|
||||
super.eventName = "InquiryResultEvent";
|
||||
results = new InquiryResult[1];
|
||||
results[0] = result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates event using an array of results.
|
||||
*
|
||||
* @param resultsArray an array of result records
|
||||
*/
|
||||
public InquiryResultEvent(InquiryResult[] resultsArray) {
|
||||
results = resultsArray;
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes this event.
|
||||
*/
|
||||
public void process() {
|
||||
BluetoothStack stack = BluetoothStack.getInstance();
|
||||
for (int i = 0; i < results.length; i++) {
|
||||
stack.onInquiryResult(results[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* 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;
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.bluetooth.UUID;
|
||||
|
||||
/*
|
||||
* JavaSDPClient class provides a client side of SDP connection as described in
|
||||
* Bluetooth Specification version 1.2.
|
||||
*/
|
||||
public class JavaSDPClient implements SDPClient{
|
||||
|
||||
/* Transport connection for this client. */
|
||||
private SDPClientConnection connection = null;
|
||||
|
||||
/* Bluetooth address this client is connected to. */
|
||||
private String address = null;
|
||||
|
||||
private Hashtable ssTrnas = new Hashtable();
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/*
|
||||
* Constructs an <code>JavaSDPClient<code> object and opens SDP connection
|
||||
* to the remote device with the specified Bluetooth address.
|
||||
*
|
||||
* @param bluetoothAddress bluetooth address of SDP server
|
||||
*/
|
||||
public JavaSDPClient(String bluetoothAddress) throws IOException {
|
||||
address = bluetoothAddress;
|
||||
try {
|
||||
connection = SDPClientConnection.getSDPClientConnection(address);
|
||||
} catch (IOException e) {
|
||||
//TODO add proper handling routines
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Closes connection of this client to the specified server.
|
||||
*
|
||||
* @throws IOException if no connection is open
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.println("* JavaSDPClient: finishing transactions");
|
||||
}
|
||||
Enumeration trs = (Enumeration) ssTrnas.keys();
|
||||
synchronized (ssTrnas) {
|
||||
while (trs.hasMoreElements()) {
|
||||
Object key = trs.nextElement();
|
||||
SDPClientTransaction tr = (SDPClientTransaction) ssTrnas
|
||||
.get(key);
|
||||
if (tr != null) {
|
||||
tr.finish();
|
||||
}
|
||||
}
|
||||
ssTrnas.clear();
|
||||
}
|
||||
if (DEBUG) {
|
||||
System.out.println("* JavaSDPClient: closing connection");
|
||||
}
|
||||
if (connection != null) {
|
||||
connection.release();
|
||||
connection = null;
|
||||
}
|
||||
if (DEBUG) {
|
||||
System.out.println("* JavaSDPClient: Canceling all receivers");
|
||||
}
|
||||
SDPClientReceiver.cancel();
|
||||
if (DEBUG) {
|
||||
System.out.println("* JavaSDPClient: closed");
|
||||
}
|
||||
}
|
||||
|
||||
public void removeTransaction( String transID ) {
|
||||
synchronized (ssTrnas) {
|
||||
ssTrnas.remove( transID );
|
||||
}
|
||||
}
|
||||
|
||||
public SDPClientConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initiates ServiceSearch transaction that is used to search for
|
||||
* services that have all the UUIDs specified on a server.
|
||||
*/
|
||||
public void serviceSearchRequest(UUID[] uuidSet, int transactionID,
|
||||
SDPResponseListener listener) throws IOException {
|
||||
SDPClientTransaction tr = null;
|
||||
tr = new ClientServiceSearchTransaction(this, transactionID, listener, uuidSet);
|
||||
synchronized (ssTrnas) {
|
||||
ssTrnas.put(tr.getID(), tr);
|
||||
}
|
||||
tr.start();
|
||||
}
|
||||
|
||||
/*
|
||||
* Initiates ServiceAttribute transaction that retrieves
|
||||
* specified attribute values from a specific service record.
|
||||
*/
|
||||
public void serviceAttributeRequest(int serviceRecordHandle, int[] attrSet,
|
||||
int transactionID, SDPResponseListener listener) throws IOException {
|
||||
SDPClientTransaction tr = new ClientServiceAttributeTransaction(this, transactionID, listener, serviceRecordHandle, attrSet);
|
||||
synchronized (ssTrnas) {
|
||||
ssTrnas.put(tr.getID(), tr);
|
||||
}
|
||||
tr.start();
|
||||
}
|
||||
|
||||
/*
|
||||
* Initiates ServiceSearchAttribute transaction that searches for services
|
||||
* on a server by UUIDs specified and retrieves values of specified
|
||||
* parameters for service records found.
|
||||
*/
|
||||
public void serviceSearchAttributeRequest(int[] attrSet, UUID[] uuidSet,
|
||||
int transactionID, SDPResponseListener listener) throws IOException {
|
||||
SDPClientTransaction tr = new ClientServiceSearchAttributeTransaction( this, transactionID, listener, attrSet, uuidSet );
|
||||
synchronized (ssTrnas) {
|
||||
ssTrnas.put(tr.getID(), tr);
|
||||
}
|
||||
tr.start();
|
||||
}
|
||||
|
||||
/*
|
||||
* Cancels transaction with given ID.
|
||||
*/
|
||||
public boolean cancelServiceSearch(int transactionID) {
|
||||
SDPClientTransaction tr = null;
|
||||
boolean isCanceled = false;
|
||||
synchronized (ssTrnas) {
|
||||
String id = "" + transactionID + "_" + SDPClientTransaction.SDP_SERVICE_SEARCH_REQUEST;
|
||||
tr = (SDPClientTransaction)ssTrnas.get( id );
|
||||
ssTrnas.remove(id);
|
||||
}
|
||||
if (tr != null) {
|
||||
tr.cancel(SDPResponseListener.TERMINATED);
|
||||
isCanceled = true;
|
||||
}
|
||||
return isCanceled;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
import com.sun.jsr082.bluetooth.BluetoothUtils;
|
||||
|
||||
/*
|
||||
* BluetoothStack implementation which relies on HCI command/event flow.
|
||||
*/
|
||||
public class JavacallBluetoothStack extends BluetoothStack {
|
||||
|
||||
/* Maximum HCI packet size as defined by Bluetooth 1.1 specification. */
|
||||
private final int MAX_HCI_PACKET_SIZE = 257;
|
||||
/* This values must be identical with Javanotify events IDs */
|
||||
/*in the btStackEvent.c file*/
|
||||
/* HCI Inquiry Complete event code. */
|
||||
protected final static int JAVACALL_BT_EVENT_INQUIRY_COMPLETE = 1;
|
||||
/* HCI Inquiry Result event code. */
|
||||
protected final static int JAVACALL_BT_EVENT_INQUIRY_RESULT = 2;
|
||||
/* HCI Authentication Complete event code. */
|
||||
protected final static int JAVACALL_BT_EVENT_AUTHENTICATION_COMPLETE = 3;
|
||||
/* HCI Remote Name Request Complete event code. */
|
||||
protected final static int JAVACALL_BT_EVENT_REMOTE_NAME_COMPLETE = 4;
|
||||
/* HCI Encrypt Change event code. */
|
||||
protected final static int JAVACALL_BT_EVENT_ENCRYPTION_CHANGE = 5;
|
||||
/* HCI Service Discovered event code */
|
||||
protected final static int JAVACALL_BT_EVENT_SERVICE_DISCOVERED = 6;
|
||||
/* HCI Service Search Completed event code */
|
||||
protected final static int JAVACALL_BT_EVENT_SERVICE_SEARCH_COMPLETED = 7;
|
||||
|
||||
/* Internal byte array for storing incoming HCI events. */
|
||||
private byte[] buffer = new byte[MAX_HCI_PACKET_SIZE];
|
||||
|
||||
/*
|
||||
* Extracts Bluetooth address from the internal buffer.
|
||||
*
|
||||
* @param offset offset in the internal buffer
|
||||
* @return Bluetooth address consisting of 12 hexadecimal characters
|
||||
*/
|
||||
protected String extractAddress(int offset) {
|
||||
byte[] addr = new byte[BluetoothUtils.BTADDR_SIZE];
|
||||
System.arraycopy(buffer, offset, addr, 0, BluetoothUtils.BTADDR_SIZE);
|
||||
return BluetoothUtils.getAddressString(addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extracts string (e.g. friendly name) from the internal buffer. The
|
||||
* string is stored in UTF-8 format and is terminated by NULL character.
|
||||
*
|
||||
* @param offset offset in the internal buffer
|
||||
* @return Java string retrieved from binary event data
|
||||
*/
|
||||
protected String extractString(int offset) {
|
||||
int length = 0;
|
||||
while (offset + length < MAX_HCI_PACKET_SIZE &&
|
||||
buffer[offset + length] != 0) {
|
||||
length++;
|
||||
}
|
||||
// IMPL_NOTE: UTF-8 encoding support for Java strings?
|
||||
// return new String(buffer, offset, length, "UTF-8");
|
||||
return stringUTF8(buffer, offset, length);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates Java String object from UTF-8 encoded string.
|
||||
*
|
||||
* @param buffer buffer containing the string in UTF-8 format
|
||||
* @param offset offset of the first character in the buffer
|
||||
* @param length length of the encoded string in bytes
|
||||
* @return Java String object containing the string in UTF-16 format
|
||||
*/
|
||||
protected static native String stringUTF8(byte[] buffer, int offset,
|
||||
int length);
|
||||
|
||||
/*
|
||||
* Extracts 2 octets from the internal buffer.
|
||||
*
|
||||
* @param offset offset in the internal buffer
|
||||
* @return 16 bits of data from the given offset
|
||||
*/
|
||||
protected int extractShort(int offset) {
|
||||
return ((int)buffer[offset] & 0xff) |
|
||||
(((int)buffer[offset + 1] & 0xff) << 8);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves Bluetooth event from HCI event data containing in the
|
||||
* internal buffer.
|
||||
*
|
||||
* @return BluetoothEvent subclass instance
|
||||
*/
|
||||
// protected BluetoothEvent retrieveEvent(Object handle) {
|
||||
protected BluetoothEvent retrieveEvent(Object handle) {
|
||||
readData(buffer);
|
||||
int code = buffer[0];
|
||||
int len = buffer[1];
|
||||
switch (code) {
|
||||
case JAVACALL_BT_EVENT_INQUIRY_COMPLETE:
|
||||
return new InquiryCompleteEvent(buffer[2] == 0);
|
||||
case JAVACALL_BT_EVENT_INQUIRY_RESULT: {
|
||||
int num = buffer[2];
|
||||
int offset = 3;
|
||||
InquiryResult[] results = new InquiryResult[num];
|
||||
for (int i = 0; i < num; i++) {
|
||||
String addr = extractAddress(offset);
|
||||
int cod = 0;
|
||||
for (int j = 0; j < 3; j++) {
|
||||
cod |= ((int)buffer[offset + 9 + j] & 0xff) <<
|
||||
(16 - j * 8);
|
||||
}
|
||||
results[i] = new InquiryResult(addr, cod);
|
||||
offset += 14; // 14 is the size of the response structure
|
||||
}
|
||||
return new InquiryResultEvent(results);
|
||||
}
|
||||
case JAVACALL_BT_EVENT_AUTHENTICATION_COMPLETE:
|
||||
return new AuthenticationCompleteEvent(extractShort(3),
|
||||
buffer[2] == 0);
|
||||
case JAVACALL_BT_EVENT_REMOTE_NAME_COMPLETE:
|
||||
return new NameResultEvent(extractAddress(3), extractString(9));
|
||||
case JAVACALL_BT_EVENT_ENCRYPTION_CHANGE:
|
||||
return new EncryptionChangeEvent(extractShort(3),
|
||||
buffer[2] == 0, buffer[5] == 1);
|
||||
// #ifdef USE_NATIVE_SDDB
|
||||
case JAVACALL_BT_EVENT_SERVICE_DISCOVERED: {
|
||||
int transID, recHandle;
|
||||
int offset = 2;
|
||||
transID = extractShort(offset) + (extractShort(offset+2) << 16);
|
||||
offset = offset + 4;
|
||||
recHandle = extractShort(offset) + (extractShort(offset+2) << 16);
|
||||
return new ServiceDiscoveredEvent(transID, recHandle);
|
||||
}
|
||||
case JAVACALL_BT_EVENT_SERVICE_SEARCH_COMPLETED: {
|
||||
int transID;
|
||||
int offset = 2;
|
||||
transID = extractShort(offset) + (extractShort(offset+2) << 16);
|
||||
return new ServiceSearchCompletedEvent(transID,
|
||||
buffer[offset+4]);
|
||||
}
|
||||
// #endif
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
import com.sun.jsr082.obex.SessionNotifierImpl;
|
||||
import javax.microedition.io.Connection;
|
||||
import javax.bluetooth.DeviceClass;
|
||||
import javax.bluetooth.DiscoveryAgent;
|
||||
import javax.bluetooth.BluetoothStateException;
|
||||
import javax.bluetooth.ServiceRecord;
|
||||
import javax.bluetooth.ServiceRegistrationException;
|
||||
|
||||
/*
|
||||
* The <code>LocalDeviceImpl</code> class is a LocalDevice
|
||||
* API class implementation. This is a singleton class.
|
||||
*/
|
||||
public final class LocalDeviceImpl {
|
||||
|
||||
/* Set to false in RR version - then the javac skip the code. */
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/* Keeps this singleton object. */
|
||||
private static LocalDeviceImpl instance;
|
||||
|
||||
/* Keeps bluetooth address of this device. */
|
||||
private String bluetoothAddress;
|
||||
|
||||
/* Timeout canceller of limited discoverable mode. */
|
||||
private CancelerOfLIAC cancelerOfLIAC = new CancelerOfLIAC();
|
||||
|
||||
/*
|
||||
* Device should not be in LIAC for more than 1 minute,
|
||||
* then return to the previous mode.
|
||||
*/
|
||||
private class CancelerOfLIAC implements Runnable {
|
||||
/* One minute. */
|
||||
private long MINUTE = 60000;
|
||||
/* Specifies the delay for timeout checks. */
|
||||
private int RETRY_DELAY = 100; // ms
|
||||
/* Saved access code to get back to at timeout. */
|
||||
private int savedCode;
|
||||
/* Keeps canceller start time to check if timeout expired. */
|
||||
private long startTime = -1;
|
||||
/* Flaggs if LIAC mode has been cancelled from outside. */
|
||||
private boolean isCanceledFromOutside = true;
|
||||
|
||||
/*
|
||||
* Starts timeout killer if new discoverable mode is LIAC.
|
||||
*
|
||||
* @param oldCode the previous value of discoverable mode.
|
||||
* @param newCode discoverable mode that has been set just.
|
||||
*/
|
||||
synchronized void notifyNewAccessCode(int oldCode, int newCode) {
|
||||
if (newCode == oldCode) {
|
||||
return;
|
||||
}
|
||||
savedCode = oldCode;
|
||||
|
||||
if (newCode == DiscoveryAgent.LIAC) {
|
||||
// the currentCode was not LIAC - start a killer
|
||||
startTime = System.currentTimeMillis();
|
||||
new Thread(this).start();
|
||||
} else {
|
||||
/*
|
||||
* startTime != -1 if the killer is running, but
|
||||
* this method may be called by the killer itself -
|
||||
* then there is no need to stop it.
|
||||
*/
|
||||
boolean stopKiller = startTime != -1 && isCanceledFromOutside;
|
||||
startTime = -1;
|
||||
isCanceledFromOutside = false;
|
||||
|
||||
if (stopKiller) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements of <code>run()</code> of <code>Runnable</code> interface.
|
||||
*/
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(RETRY_DELAY);
|
||||
} catch (InterruptedException e) {} // ignore
|
||||
|
||||
synchronized (this) {
|
||||
// the access code was changed by application
|
||||
if (startTime == -1) {
|
||||
notify();
|
||||
return;
|
||||
}
|
||||
// minute is running yet
|
||||
if (System.currentTimeMillis() - startTime < MINUTE) {
|
||||
continue;
|
||||
}
|
||||
// minute is over - change the mode back
|
||||
isCanceledFromOutside = false;
|
||||
boolean res = false;
|
||||
|
||||
try {
|
||||
res = setDiscoverable(savedCode);
|
||||
} catch (BluetoothStateException e) {}
|
||||
|
||||
if (!res) {
|
||||
// not now - h-m-m, ok, try later then
|
||||
isCanceledFromOutside = true;
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end of class 'CancelerOfLIAC' definition
|
||||
|
||||
/*
|
||||
* Constructs the only instance of <code>LocalDeviceImpl</code>.
|
||||
*/
|
||||
private LocalDeviceImpl() {
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves singleton instance.
|
||||
*
|
||||
* @return the only instance of <code>LocalDeviceImpl</code>
|
||||
* @throws BluetoothStateException if an error occured.
|
||||
*/
|
||||
public static synchronized LocalDeviceImpl getInstance()
|
||||
throws BluetoothStateException {
|
||||
if (instance == null) {
|
||||
instance = new LocalDeviceImpl();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public String getFriendlyName() {
|
||||
return BCC.getInstance().getFriendlyName();
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public DeviceClass getDeviceClass() {
|
||||
return BCC.getInstance().getDeviceClass();
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public String getProperty(String property) {
|
||||
return System.getProperty(property);
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int getDiscoverable() {
|
||||
return BCC.getInstance().getAccessCode();
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public String getBluetoothAddress() {
|
||||
return BCC.getInstance().getBluetoothAddress();
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean setDiscoverable(int accessCode)
|
||||
throws BluetoothStateException {
|
||||
// Check if the specified mode has a valid value
|
||||
if (accessCode != DiscoveryAgent.GIAC &&
|
||||
accessCode != DiscoveryAgent.LIAC &&
|
||||
accessCode != DiscoveryAgent.NOT_DISCOVERABLE &&
|
||||
(accessCode < 0x9E8B00 || accessCode > 0x9E8B3F)) {
|
||||
throw new IllegalArgumentException("Access code is out of range: "
|
||||
+ "0x" + Integer.toHexString(accessCode).toUpperCase());
|
||||
}
|
||||
synchronized (cancelerOfLIAC) {
|
||||
/*
|
||||
* Accroding to the spec, the device should only be limited
|
||||
* discoverable (DiscoveryAgent.LIAC) for 1 minute -
|
||||
* then back to the PREVIOUS discoverable mode.
|
||||
*/
|
||||
int oldAccessCode = BCC.getInstance().getAccessCode();
|
||||
if (BCC.getInstance().setAccessCode(accessCode)) {
|
||||
cancelerOfLIAC.notifyNewAccessCode(oldAccessCode, accessCode);
|
||||
if (accessCode != DiscoveryAgent.NOT_DISCOVERABLE) {
|
||||
// Start SDDB if discoverable mode was set successfully
|
||||
// IMPL_NOTE: Do we really need this step?
|
||||
SDDB.getInstance();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public ServiceRecord getRecord(Connection notifier) {
|
||||
if (notifier == null) {
|
||||
throw new NullPointerException("Null notifier specified.");
|
||||
}
|
||||
if (!(notifier instanceof BluetoothNotifier)) {
|
||||
if (!(notifier instanceof SessionNotifierImpl)) {
|
||||
throw new IllegalArgumentException("Invalid notifier class.");
|
||||
}
|
||||
Connection transport =
|
||||
((SessionNotifierImpl)notifier).getTransport();
|
||||
if (!(transport instanceof BluetoothNotifier)) {
|
||||
throw new IllegalArgumentException("Invalid notifier class.");
|
||||
}
|
||||
return ((BluetoothNotifier)transport).getServiceRecord();
|
||||
}
|
||||
return ((BluetoothNotifier)notifier).getServiceRecord();
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public void updateRecord(ServiceRecord srvRecord)
|
||||
throws ServiceRegistrationException {
|
||||
if (DEBUG) {
|
||||
System.out.println("LocalDeviceImpl.updateRecord");
|
||||
}
|
||||
if (srvRecord == null) {
|
||||
throw new NullPointerException("Null record specified.");
|
||||
}
|
||||
if (!(srvRecord instanceof ServiceRecordImpl)) {
|
||||
throw new IllegalArgumentException("Invalid service record class.");
|
||||
}
|
||||
ServiceRecordImpl record = (ServiceRecordImpl)srvRecord;
|
||||
BluetoothNotifier notifier = record.getNotifier();
|
||||
if (notifier == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Service record is not from local SDDB.");
|
||||
}
|
||||
notifier.updateServiceRecord(record);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if Bluetooth device is turned on.
|
||||
*
|
||||
* @return <code>true</code> is the Bluetooth device is on,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isPowerOn() {
|
||||
return BCC.getInstance().isBluetoothEnabled();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* Remote device's friendly name retrieval event.
|
||||
*/
|
||||
public class NameResultEvent extends BluetoothEvent {
|
||||
|
||||
/* Bluetooth address of the device. */
|
||||
private String deviceAddr;
|
||||
|
||||
/* Friendly name of the device. */
|
||||
private String deviceName;
|
||||
|
||||
/*
|
||||
* Creates this event.
|
||||
*
|
||||
* @param addr Bluetooth address of the device
|
||||
* @param name friendly name of the device
|
||||
*/
|
||||
public NameResultEvent(String addr, String name) {
|
||||
super.eventName = "NameResultEvent";
|
||||
deviceAddr = addr;
|
||||
deviceName = name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes this event immediately.
|
||||
*/
|
||||
public void dispatch() {
|
||||
BluetoothStack.getInstance().onNameRetrieve(deviceAddr, deviceName);
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes this event. Empty, since the processing was done in dispatch().
|
||||
*/
|
||||
public void process() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
import javax.bluetooth.*;
|
||||
import com.sun.jsr082.bluetooth.BluetoothStack;
|
||||
import java.util.Vector;
|
||||
|
||||
/*
|
||||
* Native-based Bluetooth Control Center. For many operations, this
|
||||
* implementation relies on BluetoothStack class.
|
||||
*/
|
||||
public class NativeBCC extends BCC {
|
||||
|
||||
/*
|
||||
* Delimiter character used to separate entries in packed strings returned
|
||||
* by <code>getPreknown()</code>.
|
||||
*/
|
||||
private final char ADDR_DELIMETER = ':';
|
||||
|
||||
|
||||
/*
|
||||
* Constructs the only instance of this class.
|
||||
*/
|
||||
protected NativeBCC() {
|
||||
initialize();
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocates native resources.
|
||||
*/
|
||||
private native void initialize();
|
||||
|
||||
/*
|
||||
* Releases native resources.
|
||||
*/
|
||||
protected native void finalize();
|
||||
|
||||
/*
|
||||
* Enables Bluetooth radio and the Bluetooth protocol stack for use.
|
||||
*
|
||||
* @return true if the operation succeeded, false otherwise
|
||||
*/
|
||||
public boolean enableBluetooth() {
|
||||
if (BluetoothStack.getInstance().isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
if (!confirmEnable()) {
|
||||
return false;
|
||||
}
|
||||
return BluetoothStack.getInstance().enable();
|
||||
}
|
||||
|
||||
/*
|
||||
* Queries the power state of the Bluetooth device.
|
||||
*
|
||||
* @return <code>true</code> is the Bluetooth device is on,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isBluetoothEnabled() {
|
||||
return BluetoothStack.getInstance().isEnabled();
|
||||
}
|
||||
|
||||
/*
|
||||
* Asks user whether Bluetooth radio is allowed to be turned on.
|
||||
*
|
||||
* @return true if user has allowed to enable Bluetooth, false otherwise
|
||||
*/
|
||||
public native boolean confirmEnable();
|
||||
|
||||
/*
|
||||
* Returns local Bluetooth address.
|
||||
*
|
||||
* @return local Bluetooth address.
|
||||
*/
|
||||
public String getBluetoothAddress() {
|
||||
return BluetoothStack.getInstance().getLocalAddress();
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines if the local device is in connectable mode.
|
||||
*
|
||||
* @return true if the device is connectable, false otherwise
|
||||
*/
|
||||
public native boolean isConnectable();
|
||||
|
||||
/*
|
||||
* Returns user-friendly name for the local device.
|
||||
*
|
||||
* @return user-friendly name for the local device, or
|
||||
* null if the name could not be retrieved
|
||||
* @see LocalDevice#getFriendlyName
|
||||
*/
|
||||
public String getFriendlyName() {
|
||||
return BluetoothStack.getInstance().getLocalName();
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves the user-friendly name for specified remote device.
|
||||
*
|
||||
* @param address Bluetooth address of a remote device
|
||||
* @return name of the remote device, or
|
||||
* null if the name could not be retrieved
|
||||
* @see RemoteDevice#getFriendlyName
|
||||
*/
|
||||
public String getFriendlyName(String address) {
|
||||
return BluetoothStack.getInstance().askFriendlyNameSync(address);
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public DeviceClass getDeviceClass() {
|
||||
return new DeviceClass(BluetoothStack.getInstance().getDeviceClass());
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean setServiceClasses(int classes) {
|
||||
return BluetoothStack.getInstance().setServiceClasses(classes);
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int getAccessCode() {
|
||||
return BluetoothStack.getInstance().getAccessCode();
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean setAccessCode(int accessCode) {
|
||||
return BluetoothStack.getInstance().setAccessCode(accessCode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if the local device has a bond with a remote device.
|
||||
*
|
||||
* @param address Bluetooth address of a remote device
|
||||
* @return true if the two devices were paired, false otherwise
|
||||
*/
|
||||
public native boolean isPaired(String address);
|
||||
|
||||
/*
|
||||
* Checks if a remote device was authenticated.
|
||||
*
|
||||
* @param address Bluetooth address of a remote device
|
||||
* @return true if the device was authenticated, false otherwise
|
||||
*/
|
||||
public native boolean isAuthenticated(String address);
|
||||
|
||||
/*
|
||||
* Checks if a remote device is trusted (authorized for all services).
|
||||
*
|
||||
* @param address Bluetooth address of a remote device
|
||||
* @return true if the device is trusted, false otherwise
|
||||
*/
|
||||
public native boolean isTrusted(String address);
|
||||
|
||||
/*
|
||||
* Checks if connections to a remote device are encrypted.
|
||||
*
|
||||
* @param address Bluetooth address of the remote device
|
||||
* @return true if connections to the device are encrypted, false otherwise
|
||||
*/
|
||||
public native boolean isEncrypted(String address);
|
||||
|
||||
/*
|
||||
* Retrieves PIN code to use for pairing with a remote device. If the
|
||||
* PIN code is not known, PIN entry dialog is displayed.
|
||||
*
|
||||
* @param address the Bluetooth address of the remote device
|
||||
* @return string containing the PIN code
|
||||
*/
|
||||
public native String getPasskey(String address);
|
||||
|
||||
/*
|
||||
* Initiates pairing with a remote device.
|
||||
*
|
||||
* @param address the Bluetooth address of the device with which to pair
|
||||
* @param pin an array containing the PIN code
|
||||
* @return true if the device was authenticated, false otherwise
|
||||
*/
|
||||
public native boolean bond(String address, String pin);
|
||||
|
||||
/*
|
||||
* Authenticates remote device.
|
||||
*
|
||||
* @param address Bluetooth address of a remote device
|
||||
* @return true if the device was authenticated, false otherwise
|
||||
*/
|
||||
public boolean authenticate(String address) {
|
||||
if (isAuthenticated(address)) {
|
||||
return true;
|
||||
}
|
||||
if (!isPaired(address)) {
|
||||
String pin = getPasskey(address);
|
||||
if (pin == null || !bond(address, pin)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return BluetoothStack.getInstance().authenticateSync(address);
|
||||
}
|
||||
|
||||
/*
|
||||
* Authorizes a Bluetooth connection.
|
||||
*
|
||||
* @param address Bluetooth address of a remote device
|
||||
* @param handle handle for the service record of the srvice the remote
|
||||
* device is trying to access
|
||||
* @return true if authorization succeeded, false otherwise
|
||||
*/
|
||||
public native boolean authorize(String address, int handle);
|
||||
|
||||
/*
|
||||
* Enables or disables encryption of data exchanges.
|
||||
*
|
||||
* @param address the Bluetooth address of the remote device
|
||||
* @param enable indicated whether the encryption needs to be enabled
|
||||
* @return true if the encryption has been changed, false otherwise
|
||||
*/
|
||||
public boolean encrypt(String address, boolean enable) {
|
||||
if (setEncryption(address, enable)) {
|
||||
return BluetoothStack.getInstance().encryptSync(address, enable);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns list of preknown devices in a packed string.
|
||||
*
|
||||
* @return vector containing preknown devices
|
||||
*/
|
||||
public Vector getPreknownDevices() {
|
||||
return listDevices(getPreknown());
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns list of preknown devices in a packed string.
|
||||
*
|
||||
* @return packed string containing preknown devices
|
||||
*/
|
||||
private native String getPreknown();
|
||||
|
||||
/*
|
||||
* Extracts Bluetooth addresses from a packed string.
|
||||
* In the packed string, each device entry is a Bluetooth device address
|
||||
* followed by <code>ADDR_DELIMETER</code> delimiter.
|
||||
*
|
||||
* @param packed string containing Bluetooth addresses
|
||||
* @return Vector containing Bluetooth addresses
|
||||
*/
|
||||
private Vector listDevices(String packed) {
|
||||
if (packed == null || packed.trim().length() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Vector addrs = new Vector();
|
||||
int index = 0;
|
||||
while (index < packed.length()) {
|
||||
int end = packed.indexOf(ADDR_DELIMETER, index);
|
||||
if (end == -1) {
|
||||
end = packed.length();
|
||||
}
|
||||
addrs.addElement(packed.substring(index, end));
|
||||
index = end + 1;
|
||||
}
|
||||
return addrs;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if there is a connection to the remote device.
|
||||
*
|
||||
* @param address the Bluetooth address of the remote device
|
||||
* @return true if connection is established with the remote device
|
||||
*/
|
||||
public native boolean isConnected(String address);
|
||||
|
||||
/*
|
||||
* Increases or decreases encryption request counter for a remote device.
|
||||
*
|
||||
* @param address the Bluetooth address of the remote device
|
||||
* @param enable indicated whether the encryption needs to be enabled
|
||||
* @return true if the encryption needs to been changed, false otherwise
|
||||
*/
|
||||
public native boolean setEncryption(String address, boolean enable);
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
import javax.bluetooth.RemoteDevice;
|
||||
|
||||
/*
|
||||
* Extends <code>RemoteDevice</code> class to allow creating
|
||||
* instances in this package.
|
||||
*/
|
||||
final public class RemoteDeviceImpl extends RemoteDevice {
|
||||
/*
|
||||
* Creates an instance with address given.
|
||||
*
|
||||
* @param address Bluetooth address of the device
|
||||
* @see javax.bluetooth.RemoteDevice#RemoteDevice(String)
|
||||
*/
|
||||
public RemoteDeviceImpl(String address) {
|
||||
super(address);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
import javax.bluetooth.ServiceRegistrationException;
|
||||
import com.sun.jsr082.bluetooth.ServiceRecordSerializer;
|
||||
// #ifndef NO_PUSH
|
||||
import com.sun.jsr082.bluetooth.BluetoothPush;
|
||||
// #endif
|
||||
|
||||
|
||||
/*
|
||||
* This class represents service discovery database (SDDB).
|
||||
*/
|
||||
public final class SDDB {
|
||||
|
||||
/* The only instance of this class. */
|
||||
private static SDDB instance = null;
|
||||
|
||||
/* Helper object for ServiveRecord serialization. */
|
||||
private ServiceRecordSerializer srs = new ServiceRecordSerializer();
|
||||
|
||||
/*
|
||||
* Retrieves the SDDB instance, initializes if needed.
|
||||
*
|
||||
* Due to SDP uses java implementation of L2CAPConnection,
|
||||
* we may initialize SDP only when LocalDevice is initialized,
|
||||
* and application creates either notifier or connection.
|
||||
*
|
||||
* @return the SDDB instance
|
||||
*/
|
||||
public static synchronized SDDB getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new SDDB();
|
||||
// #ifdef NO_PUSH
|
||||
// initialize SDDB
|
||||
initialize();
|
||||
// #endif
|
||||
|
||||
// #ifdef USE_JSR_82_EMULATOR
|
||||
// IMPL_NOTE move to proper place when moving
|
||||
// emulation below porting layer completed
|
||||
com.sun.midp.jsr82emul.EmulationPolling.start();
|
||||
// #endif
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates an instance of this class.
|
||||
*/
|
||||
private SDDB() {
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates or updates a service record in the SDDB.
|
||||
*
|
||||
* For records whose handle is 0, a new entry in SDDB is created.
|
||||
* Otherwise, an existing entry is updated.
|
||||
*
|
||||
* @param record service record to be added to or updated in the database
|
||||
* @throws ServiceRegistrationException if an error occured while
|
||||
* updating SDDB
|
||||
*/
|
||||
public void updateServiceRecord(ServiceRecordImpl record)
|
||||
throws ServiceRegistrationException {
|
||||
int oldHandle = record.getHandle();
|
||||
int classes = record.getDeviceServiceClasses();
|
||||
if (oldHandle == 0) {
|
||||
record.setHandle(0);
|
||||
}
|
||||
byte[] data = srs.serialize(record);
|
||||
int newHandle = updateRecord(oldHandle, classes, data);
|
||||
if (newHandle == 0) {
|
||||
throw new ServiceRegistrationException(
|
||||
"Error updating service record.");
|
||||
}
|
||||
if (newHandle != oldHandle) {
|
||||
record.setHandle(newHandle);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes record from the database.
|
||||
*
|
||||
* @param record the service record that indicates one to be removed from
|
||||
* the database
|
||||
*/
|
||||
public void removeServiceRecord(ServiceRecordImpl record) {
|
||||
removeRecord(record.getHandle());
|
||||
record.setHandle(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if (a copy of) given record is already in the SDDB.
|
||||
* The record is cosidered to be in the SDDB if the database contains
|
||||
* a record with the same handle.
|
||||
*
|
||||
* @param record the record to be tested
|
||||
*/
|
||||
public boolean contains(ServiceRecordImpl record) {
|
||||
return readRecord(record.getHandle(), null) > 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves handles of all the records in the database as array.
|
||||
*
|
||||
* @return array of int that contains handles of all the service records
|
||||
* from the database, each once and only them
|
||||
*/
|
||||
int[] getHandles() {
|
||||
int size = getRecords(null);
|
||||
int handles[] = new int[size];
|
||||
getRecords(handles);
|
||||
return handles;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves a service record from the database by the handle value.
|
||||
*
|
||||
* @param handle the handle value to get service record by
|
||||
* @return a reference to the record in the database if one with handle
|
||||
* given presents, <code>null</code> otherwise
|
||||
*/
|
||||
public ServiceRecordImpl getServiceRecord(int handle) {
|
||||
return getServiceRecord(handle, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves a service record from the database by the handle value.
|
||||
*
|
||||
* @param handle the handle value to get service record by
|
||||
* @param notifier Bluetooth notifier to be used with the service record
|
||||
* @return a reference to the record in the database if one with handle
|
||||
* given presents, <code>null</code> otherwise
|
||||
*/
|
||||
public ServiceRecordImpl getServiceRecord(int handle,
|
||||
BluetoothNotifier notifier) {
|
||||
int size = readRecord(handle, null);
|
||||
if (size == 0) {
|
||||
return null;
|
||||
}
|
||||
byte[] data = new byte[size];
|
||||
readRecord(handle, data);
|
||||
ServiceRecordImpl record = srs.restore(notifier, data);
|
||||
record.setDeviceServiceClasses(getServiceClasses(handle));
|
||||
record.setHandle(handle);
|
||||
return record;
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates or updates service record in the SDDB.
|
||||
*
|
||||
* @param handle handle of the service record to be updated;
|
||||
* if equals to 0, a new record will be created
|
||||
* @param classes device service classes associated with the record
|
||||
* @param data binary data containing attribute-value pairs in the format
|
||||
* identical to the one used in the AttributeList parameter of
|
||||
* the SDP_ServiceAttributeResponse PDU
|
||||
* @return service record handle, or 0 if the operation fails
|
||||
*/
|
||||
public native int updateRecord(int handle, int classes, byte[] data);
|
||||
|
||||
/*
|
||||
* Removes service record from the SDDB.
|
||||
*
|
||||
* @param handle hanlde of the service record to be deleted
|
||||
*/
|
||||
public native void removeRecord(int handle);
|
||||
|
||||
/*
|
||||
* Retrieves service record from the SDDB.
|
||||
*
|
||||
* @param handle handle of the service record to be retrieved
|
||||
* @param data byte array which will receive the data,
|
||||
* or null for size query
|
||||
* @return size of the data read/required
|
||||
*/
|
||||
private native int readRecord(int handle, byte[] data);
|
||||
|
||||
/*
|
||||
* Retrieves service classes of a record in the SDDB.
|
||||
*
|
||||
* @param handle handle of the service record
|
||||
* @return service classes set for the record
|
||||
*/
|
||||
private native int getServiceClasses(int handle);
|
||||
|
||||
/*
|
||||
* Retrieves handles of all service records in the SDDB.
|
||||
*
|
||||
* @param handles array to receive handles, or null for count query
|
||||
* @return number of entries read/available
|
||||
*/
|
||||
private native int getRecords(int[] handles);
|
||||
|
||||
// #ifdef NO_PUSH
|
||||
/*
|
||||
* Native initializer (opens SDDB)
|
||||
*
|
||||
* Needed only if there is no push
|
||||
*/
|
||||
private static native void initialize();
|
||||
|
||||
/*
|
||||
* Native finalizer (closes SDDB)
|
||||
*
|
||||
* Needed only if there is no push
|
||||
*/
|
||||
protected native void finalize();
|
||||
// #endif
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.bluetooth.UUID;
|
||||
import javax.microedition.io.Connector;
|
||||
import javax.microedition.io.Connection;
|
||||
import com.sun.jsr082.bluetooth.btl2cap.Protocol;
|
||||
|
||||
/*
|
||||
* Contains common Service Discovery Protocol data.
|
||||
*/
|
||||
public class SDP {
|
||||
/* Internal security token that grants access to restricted API. */
|
||||
// private static SecurityToken internalSecurityToken = null;
|
||||
|
||||
/* Special object used by SDP to authenticate internal URL's. */
|
||||
private static Object systemToken = new Object();
|
||||
|
||||
/* SDP UUID. */
|
||||
public static final String UUID = new UUID(0x0001).toString();
|
||||
|
||||
/* SDP server PSM. This value is defined by Bluetooth specification */
|
||||
public static final int PSM = 0x0001;
|
||||
|
||||
/*
|
||||
* Initializes internal security token. Called by internal MIDP
|
||||
* initialization routines.
|
||||
*
|
||||
* @param token internal security token that grants access to restricted API
|
||||
*/
|
||||
/*
|
||||
public static void initSecurityToken(SecurityToken token) {
|
||||
if (internalSecurityToken == null) {
|
||||
internalSecurityToken = token;
|
||||
}
|
||||
}
|
||||
*/
|
||||
/*
|
||||
* Creates and opens btl2cap connection for using by SDP.
|
||||
*
|
||||
* @param name btl2cap connection URL without protocol name
|
||||
* @return required connection instance, that is
|
||||
* <code>L2CAPConnection</code> for client or
|
||||
* <code>L2CAPConnectionNotifier</code> for server
|
||||
* @exception IOException if connection fails
|
||||
*/
|
||||
static Connection getL2CAPConnection(String name) throws IOException {
|
||||
BluetoothUrl url = new BluetoothUrl(BluetoothUrl.L2CAP, name, systemToken);
|
||||
Protocol l2cap = new Protocol();
|
||||
return l2cap.openPrim(url, Connector.READ_WRITE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks that given token is an internal one. Used to authenticate
|
||||
* an URL as generated by SDP.
|
||||
* @param token token to check
|
||||
* @return <code>true</code> if the object given is exactly the internal
|
||||
* system token, <code>false</code> otherwise
|
||||
*/
|
||||
public static boolean checkSystemToken(Object token) {
|
||||
return token == systemToken;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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;
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.bluetooth.UUID;
|
||||
|
||||
/*
|
||||
* SDPClient class provides a client side of SDP connection as described in
|
||||
* Bluetooth Specification version 1.2.
|
||||
*/
|
||||
public interface SDPClient {
|
||||
|
||||
/*
|
||||
* Closes connection of this client to the specified server.
|
||||
*
|
||||
* @throws IOException if no connection is open
|
||||
*/
|
||||
public void close() throws IOException;
|
||||
|
||||
/*
|
||||
* Initiates ServiceAttribute transaction that retrieves
|
||||
* specified attribute values from a specific service record.
|
||||
*/
|
||||
public void serviceAttributeRequest(int serviceRecordHandle, int[] attrSet,
|
||||
int transactionID, SDPResponseListener listener) throws IOException;
|
||||
|
||||
/*
|
||||
* Initiates ServiceSearchAttribute transaction that searches for services
|
||||
* on a server by UUIDs specified and retrieves values of specified
|
||||
* parameters for service records found.
|
||||
*/
|
||||
public void serviceSearchAttributeRequest(int[] attrSet, UUID[] uuidSet,
|
||||
int transactionID, SDPResponseListener listener) throws IOException;
|
||||
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import javax.bluetooth.DataElement;
|
||||
import javax.bluetooth.L2CAPConnection;
|
||||
import javax.bluetooth.UUID;
|
||||
|
||||
public class SDPClientConnection {
|
||||
/* L2CAP URL starting string. */
|
||||
private static final String SDP_L2CAP_URL_BEGIN = "//";
|
||||
|
||||
/* L2CAP URL trailing string. */
|
||||
private static final String SDP_L2CAP_URL_END = ":0001";
|
||||
|
||||
private static Hashtable connections = new Hashtable();
|
||||
|
||||
/* Bluetooth address of the server. */
|
||||
private String address;
|
||||
|
||||
/*
|
||||
* Reference counter keeps the number of SDP connections which
|
||||
* use this transport. When this value reaches zero, the L2CAP
|
||||
* connection is closed and the transport is removed from the global
|
||||
* SDPClient.transportStorage hashtable.
|
||||
*/
|
||||
private int refCount = 0;
|
||||
|
||||
/* The L2CAP (logical link) connection. */
|
||||
private L2CAPConnection connection;
|
||||
|
||||
/*
|
||||
* Object that performs reading from and writing to L2CAP connection.
|
||||
*/
|
||||
private DataL2CAPReaderWriter rw;
|
||||
|
||||
/* Lock for synchronizing reading from connection. */
|
||||
public Object readLock = new Object();
|
||||
|
||||
/* Lock for synchronizing writing to connection. */
|
||||
public Object writeLock = new Object();
|
||||
|
||||
|
||||
/*
|
||||
* Constructs <code>SDPClientConnection</code> instance that reflects transport
|
||||
* connections to the specified server.
|
||||
*
|
||||
* @param bluetoothAddress bluetooth address of the server
|
||||
*/
|
||||
protected SDPClientConnection(String bluetoothAddress) throws IOException {
|
||||
address = bluetoothAddress;
|
||||
connection = (L2CAPConnection)SDP.getL2CAPConnection(
|
||||
SDP_L2CAP_URL_BEGIN + bluetoothAddress + SDP_L2CAP_URL_END);
|
||||
rw = new DataL2CAPReaderWriter(connection);
|
||||
}
|
||||
|
||||
public static SDPClientConnection getSDPClientConnection( String bluetoothAddress ) throws IOException {
|
||||
SDPClientConnection result = null;
|
||||
synchronized (connections) {
|
||||
result = (SDPClientConnection)connections.get(bluetoothAddress);
|
||||
if( result == null ) {
|
||||
result = new SDPClientConnection( bluetoothAddress );
|
||||
connections.put(bluetoothAddress, result);
|
||||
result.addRef();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void closeAll() {
|
||||
synchronized (connections) {
|
||||
Enumeration addrs = connections.keys();
|
||||
while ( addrs.hasMoreElements() ) {
|
||||
String addr = (String)addrs.nextElement();
|
||||
if (addr != null) {
|
||||
SDPClientConnection conn = (SDPClientConnection)connections.get(addr);
|
||||
if (conn != null) {
|
||||
conn.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
connections.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Increases reference counter. This object and the underlying L2CAP
|
||||
* connection will live while the counter is positive.
|
||||
*/
|
||||
protected synchronized void addRef() {
|
||||
refCount++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decreases reference counter. If the counter becomes equal to zero,
|
||||
* L2CAP connection is closed and the transport is removed from the
|
||||
* global SDPClient.transportStorage hashtable.
|
||||
*/
|
||||
public synchronized void release() {
|
||||
refCount--;
|
||||
if (refCount <= 0) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (IOException e) {
|
||||
// just ignore it, we're done with this object anyway
|
||||
}
|
||||
synchronized (connections) {
|
||||
connections.remove(this.address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DataL2CAPReaderWriter getReaderWriter() {
|
||||
return rw;
|
||||
}
|
||||
|
||||
public L2CAPConnection getL2CAPConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
|
||||
public class SDPClientReceiver implements Runnable {
|
||||
|
||||
private static Hashtable receivers = new Hashtable();
|
||||
private static int instanceCount = 0;
|
||||
private JavaSDPClient client = null;
|
||||
private static final boolean DEBUG= false;
|
||||
|
||||
/*
|
||||
* Identifies if receiving thread is running (false) or not (true).
|
||||
*/
|
||||
private Thread receiverThread = null;
|
||||
|
||||
protected SDPClientReceiver(JavaSDPClient client) {
|
||||
this.client = client;
|
||||
receiverThread = new Thread(this);
|
||||
receiverThread.start();
|
||||
if (DEBUG) {
|
||||
System.out.println("# Receiver internal thread started");
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void start(JavaSDPClient client) {
|
||||
SDPClientReceiver receiver = (SDPClientReceiver)receivers.get(client);
|
||||
if (receiver == null) {
|
||||
receiver = new SDPClientReceiver(client);
|
||||
receivers.put(client, receiver);
|
||||
instanceCount = 1;
|
||||
} else {
|
||||
instanceCount++;
|
||||
}
|
||||
if (DEBUG) {
|
||||
System.out.println("# Receiver[" + instanceCount + "] started");
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void stop(JavaSDPClient client) {
|
||||
SDPClientReceiver receiver = (SDPClientReceiver)receivers.get(client);
|
||||
if (receiver == null) {
|
||||
return;
|
||||
}
|
||||
if (DEBUG) {
|
||||
System.out.println("# Receiver[" + instanceCount + "] stopped");
|
||||
}
|
||||
if (--instanceCount > 0) {
|
||||
return;
|
||||
} else {
|
||||
receiver.finish(client.getConnection());
|
||||
receivers.remove(client);
|
||||
}
|
||||
}
|
||||
|
||||
/* Cancels receiving responses. */
|
||||
public static synchronized void cancel() {
|
||||
Enumeration clientRefs = receivers.keys();
|
||||
while( clientRefs.hasMoreElements() ) {
|
||||
Object ref = clientRefs.nextElement();
|
||||
if (ref != null) {
|
||||
SDPClientReceiver rvr = (SDPClientReceiver)receivers.get(ref);
|
||||
Thread tmp = rvr.receiverThread;
|
||||
rvr.finish(((JavaSDPClient)ref).getConnection());
|
||||
try {
|
||||
tmp.join();
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
if (DEBUG) {
|
||||
System.out.println("# Receiver internal thread stopped");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
receivers.clear();
|
||||
if (DEBUG) {
|
||||
System.out.println("# Receiver[all] canceled");
|
||||
}
|
||||
}
|
||||
|
||||
protected synchronized void finish(SDPClientConnection conn) {
|
||||
if (receiverThread != null) {
|
||||
Thread tmp = receiverThread;
|
||||
receiverThread = null;
|
||||
if (conn != null) {
|
||||
conn.release();
|
||||
if (DEBUG) {
|
||||
System.out.println("# Receiver: Connection released");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The <code>run()</code> method.
|
||||
*
|
||||
* @see java.lang.Runnable
|
||||
*/
|
||||
public void run() {
|
||||
Thread current = Thread.currentThread();
|
||||
while (client != null && client.getConnection() != null && current == receiverThread ) {
|
||||
try {
|
||||
byte pduID = client.getConnection().getReaderWriter().readByte();
|
||||
short transID = client.getConnection().getReaderWriter().readShort();
|
||||
short length = client.getConnection().getReaderWriter().readShort();
|
||||
SDPClientTransaction trans = SDPClientTransaction.findTransaction(transID);
|
||||
if (trans != null) {
|
||||
if (DEBUG) {
|
||||
System.out.println( "# Receiver processResponse:" + trans.getID() );
|
||||
}
|
||||
trans.processResponse(pduID, length);
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
System.out.println("#Receiver transaction: " + transID + " not found");
|
||||
}
|
||||
// transaction we are not aware of; skip this pdu
|
||||
client.getConnection().getReaderWriter().readBytes(length);
|
||||
if (current == receiverThread) {
|
||||
throw new IOException("Invalid transaction id: " + transID);
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
if (current == receiverThread) {
|
||||
SDPClientTransaction.cancelAll(SDPResponseListener.IO_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
receiverThread = null;
|
||||
if (DEBUG) {
|
||||
System.out.println( "# Receiver internal thread exit" );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,352 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
|
||||
/*
|
||||
* This abstract class provides base functionality for all SDP
|
||||
* transactions.
|
||||
*/
|
||||
public abstract class SDPClientTransaction extends SDPClientTransactionBase implements Runnable {
|
||||
|
||||
protected static final boolean DEBUG = false;
|
||||
|
||||
/* PDU ID (see Bluetooth Specification 1.2 Vol 3 page 131) */
|
||||
byte pduID;
|
||||
/* Transcation ID used to identify this transaction. */
|
||||
int ssTransID;
|
||||
/* Effective transaction ID. */
|
||||
int pduTransID;
|
||||
/* Length of all parameters. */
|
||||
long parameterLength;
|
||||
/* Continuation state used with partial responses. */
|
||||
byte[] continuationState = null;
|
||||
/* Listener to report request result to. */
|
||||
SDPResponseListener listener;
|
||||
/* Maps transaction IDs to ServiceTransaction objects. */
|
||||
private static Hashtable transactions = new Hashtable();
|
||||
|
||||
protected JavaSDPClient client = null;
|
||||
|
||||
public static SDPClientTransaction findTransaction( int pduTransactionID ) {
|
||||
SDPClientTransaction result = null;
|
||||
if (pduTransactionID > 0) {
|
||||
synchronized (transactions) {
|
||||
result = (SDPClientTransaction)transactions.get( new Integer( pduTransactionID ) );
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cancels all current transactions. Called in case of I/O failure
|
||||
* in the underlying L2CAP connection.
|
||||
*/
|
||||
public static void cancelAll(int reason) {
|
||||
// force actual release of resources
|
||||
synchronized (transactions) {
|
||||
Enumeration e = transactions.keys();
|
||||
while (e.hasMoreElements()) {
|
||||
Integer id = (Integer) e
|
||||
.nextElement();
|
||||
if (id != null) {
|
||||
SDPClientTransaction tr = (SDPClientTransaction)transactions.get(id);
|
||||
if (tr != null) {
|
||||
tr.cancel(reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
transactions.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class constructor.
|
||||
*
|
||||
* @param pduID protocol data unit ID
|
||||
* @param ssTransactionID transaction ID of the first request
|
||||
* @param listener listener object which will receive
|
||||
* completion and error notifications
|
||||
*/
|
||||
public SDPClientTransaction(JavaSDPClient client, int pduID, int ssTransactionID,
|
||||
SDPResponseListener listener) {
|
||||
this.pduID = (byte)pduID;
|
||||
this.ssTransID = ssTransactionID;
|
||||
pduTransID = newTransactionID();
|
||||
this.listener = listener;
|
||||
this.client = client;
|
||||
if (DEBUG) {
|
||||
System.out.println(" Transaction[" + getID() + "] created");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the effective transaction ID with a new value.
|
||||
*/
|
||||
private void updatePduTransactionID() {
|
||||
synchronized (transactions) {
|
||||
transactions.remove(new Integer(pduTransID));
|
||||
pduTransID = newTransactionID();
|
||||
transactions.put(new Integer(pduTransID), this);
|
||||
}
|
||||
}
|
||||
|
||||
public String getID() {
|
||||
return ("" + ssTransID + "_" + pduID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Starts this transaction.
|
||||
*
|
||||
* @throws IOException when an I/O error occurs
|
||||
*/
|
||||
public void run() {
|
||||
if (client == null) {
|
||||
return;
|
||||
}
|
||||
synchronized (transactions) {
|
||||
transactions.put( new Integer( pduTransID ), this);
|
||||
}
|
||||
continuationState = null;
|
||||
SDPClientReceiver.start(client);
|
||||
try {
|
||||
submitRequest();
|
||||
if (DEBUG) {
|
||||
System.out.println( "Transaction[" + getID() + "]: request sent. Waiting completion" );
|
||||
}
|
||||
synchronized (this) {
|
||||
wait();
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
ie.printStackTrace();
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
} finally {
|
||||
finish();
|
||||
if (DEBUG) {
|
||||
System.out.println( "Transaction[" + getID() + "] ended" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
(new Thread(this)).start();
|
||||
}
|
||||
|
||||
/*
|
||||
* Terminates this transaction and reports error to the listener.
|
||||
*
|
||||
* @param reason error code which will be reported
|
||||
*/
|
||||
public void cancel(int reason) {
|
||||
listener.errorResponse(reason, "", ssTransID);
|
||||
finish();
|
||||
if (DEBUG) {
|
||||
System.out.println( "Transaction[" + getID() + "]: canceled" );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ends this transaction by unregistering it in the outer class.
|
||||
*/
|
||||
public void finish() {
|
||||
synchronized (transactions) {
|
||||
transactions.remove(new Integer(pduTransID));
|
||||
}
|
||||
if (client != null) {
|
||||
client.removeTransaction(getID());
|
||||
}
|
||||
SDPClientReceiver.stop(client);
|
||||
synchronized (this) {
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads error PDU, ends this transaction and reports listener the
|
||||
* error code retrieved.
|
||||
*
|
||||
* @param length length of PDU's parameters
|
||||
*/
|
||||
public void error(int length) throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.println( "Transaction[" + getID() + "] notify error" );
|
||||
}
|
||||
short errorCode = client.getConnection().getReaderWriter().readShort();
|
||||
byte[] infoBytes = client.getConnection().getReaderWriter().readBytes(length - 2);
|
||||
listener.errorResponse(errorCode, new String(infoBytes),
|
||||
ssTransID);
|
||||
finish();
|
||||
}
|
||||
|
||||
/*
|
||||
* Completes the transaction by calling corresponding listener's
|
||||
* method with the data retrieved.
|
||||
*/
|
||||
abstract void complete();
|
||||
|
||||
/*
|
||||
* Writes transaction-specific parameters into the PDU.
|
||||
*
|
||||
* @throws IOException when an I/O error occurs
|
||||
*/
|
||||
abstract void writeParameters() throws IOException;
|
||||
|
||||
/*
|
||||
* Reads transaction-specific parameters from the PDU.
|
||||
*
|
||||
* @param length length of PDU's parameters
|
||||
* @throws IOException when an I/O error occurs
|
||||
*/
|
||||
abstract void readParameters(int length) throws IOException;
|
||||
|
||||
/*
|
||||
* Gets next SDP server response, if any, and passes it to the
|
||||
* corresponding listener. If a response is received, the transaction
|
||||
* it belongs to is stopped.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
boolean processResponse(byte pduID, short length ) throws IOException {
|
||||
boolean completed = false;
|
||||
try {
|
||||
synchronized (client.getConnection().readLock) {
|
||||
if (DEBUG) {
|
||||
System.out.println("Transaction[" + getID() + "] pdu_id: " + pduID + " response " + length + " bytes received. processing...");
|
||||
}
|
||||
|
||||
if (pduID == SDPClientTransaction.SDP_ERROR_RESPONSE) {
|
||||
if (DEBUG) {
|
||||
System.out.println("Transaction[" + getID() + "]: Error response received");
|
||||
}
|
||||
error(length);
|
||||
return true;
|
||||
}
|
||||
if (DEBUG) {
|
||||
System.out.println("Transaction[" + getID() + "] read parameters...");
|
||||
}
|
||||
readParameters(length);
|
||||
if (DEBUG) {
|
||||
System.out.println("Transaction[" + getID() + "] read continuation state...");
|
||||
}
|
||||
completed = readContinuationState();
|
||||
if (DEBUG) {
|
||||
System.out.println("Transaction[" + getID() + "] request should" + (completed ? "n't" : "") + " be resubmitted");
|
||||
}
|
||||
}
|
||||
continueResponseProcessing();
|
||||
} finally {
|
||||
if (completed) {
|
||||
synchronized (this) {
|
||||
notify();
|
||||
}
|
||||
if (DEBUG) {
|
||||
System.out.println("Transaction[" + getID() + "] finished");
|
||||
}
|
||||
}
|
||||
}
|
||||
return completed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes this transaction by either re-submitting the original
|
||||
* request if the last response was incomplete, or providing the
|
||||
* listener with the results if the transaction was completed.
|
||||
*
|
||||
* @throws IOException when an I/O error occurs
|
||||
*/
|
||||
private void continueResponseProcessing() throws IOException {
|
||||
if (continuationState != null) {
|
||||
try {
|
||||
if (DEBUG) {
|
||||
System.out.println("Transaction[" + getID() + "] resubmitt request");
|
||||
}
|
||||
resubmitRequest();
|
||||
} catch (IOException e) {
|
||||
if (DEBUG) {
|
||||
System.out.println("Transaction[" + getID() + "] error: " + e);
|
||||
}
|
||||
cancel(SDPResponseListener.IO_ERROR);
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
System.out.println( "Transaction[" + getID() + "] notify completed" );
|
||||
}
|
||||
complete();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-submits the original request with continuation state
|
||||
* data received with the incomplete response.
|
||||
*
|
||||
* @throws IOException when an I/O error occurs
|
||||
*/
|
||||
private void submitRequest() throws IOException {
|
||||
synchronized (client.getConnection().writeLock) {
|
||||
client.getConnection().getReaderWriter().writeByte(pduID);
|
||||
client.getConnection().getReaderWriter().writeShort((short)pduTransID);
|
||||
client.getConnection().getReaderWriter().writeShort((short)(parameterLength + 1));
|
||||
writeParameters();
|
||||
if (continuationState != null) {
|
||||
client.getConnection().getReaderWriter().writeByte((byte)continuationState.length);
|
||||
client.getConnection().getReaderWriter().writeBytes(continuationState);
|
||||
} else {
|
||||
client.getConnection().getReaderWriter().writeByte((byte)0x00);
|
||||
}
|
||||
client.getConnection().getReaderWriter().flush();
|
||||
if (DEBUG) {
|
||||
System.out.println("Transaction[" + getID() + "] request " + parameterLength + 1 + " bytes sent");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resubmitRequest() throws IOException {
|
||||
updatePduTransactionID();
|
||||
submitRequest();
|
||||
}
|
||||
|
||||
/*
|
||||
* Extracts continuation state parameter.
|
||||
*
|
||||
* @return true if the continuation state is present,
|
||||
* false otherwise
|
||||
* @throws IOException when an I/O error occurs
|
||||
*/
|
||||
private boolean readContinuationState() throws IOException {
|
||||
byte infoLength = client.getConnection().getReaderWriter().readByte();
|
||||
if (infoLength == 0) {
|
||||
continuationState = null;
|
||||
} else {
|
||||
continuationState = client.getConnection().getReaderWriter().readBytes(infoLength);
|
||||
}
|
||||
return (infoLength == 0);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
|
||||
/*
|
||||
* This abstract class provides base functionality for all SDP
|
||||
* transactions.
|
||||
*/
|
||||
public abstract class SDPClientTransactionBase {
|
||||
/*
|
||||
* Helper object which serializes and restores
|
||||
* <code>DataElement</code>s.
|
||||
*/
|
||||
protected DataElementSerializer des = new DataElementSerializer();
|
||||
|
||||
/* ID of SDP_ErrorResponse protocol data unit. */
|
||||
public static final int SDP_ERROR_RESPONSE = 0x01;
|
||||
|
||||
/* ID of SDP_ServiceSearchRequest protocol data unit. */
|
||||
public static final int SDP_SERVICE_SEARCH_REQUEST = 0x02;
|
||||
|
||||
/* ID of SDP_ServiceSearchResponse protocol data unit. */
|
||||
public static final int SDP_SERVICE_SEARCH_RESPONSE = 0x03;
|
||||
|
||||
/* ID of SDP_ServiceAttributeRequest protocol data unit. */
|
||||
public static final int SDP_SERVICE_ATTRIBUTE_REQUEST = 0x04;
|
||||
|
||||
/* ID of SDP_ServiceAttributeResponse protocol data unit. */
|
||||
public static final int SDP_SERVICE_ATTRIBUTE_RESPONSE = 0x05;
|
||||
|
||||
/* ID of SDP_ServiceSearchAttributeRequest protocol data unit. */
|
||||
public static final int SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST = 0x06;
|
||||
|
||||
/* ID of SDP_ServiceSearchAttributeResponse protocol data unit. */
|
||||
public static final int SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE = 0x07;
|
||||
|
||||
/* Max retrievable service record handles. Maybe defined via property. */
|
||||
public static final int MAX_SERVICE_RECORD_COUNT = 0x0fff;
|
||||
|
||||
/* Max total size of retrievable attributes. Maybe defined via property. */
|
||||
public static final int MAX_ATTRIBUTE_BYTE_COUNT = 0xffff;
|
||||
|
||||
/*
|
||||
* The lowest possible value of transaction ID.
|
||||
* The number must be positive.
|
||||
*/
|
||||
public static final int firstTransactionID = 0x0001;
|
||||
|
||||
/* The maximum possible value of transaction ID. */
|
||||
public static final int maxTransactionID = 0xffff;
|
||||
|
||||
/* Next transaction ID. */
|
||||
protected static int effectiveTransactionID = firstTransactionID;
|
||||
|
||||
/*
|
||||
* Retrieves next new transaction ID.
|
||||
*
|
||||
* @return new transaction ID.
|
||||
*/
|
||||
public static synchronized short newTransactionID() {
|
||||
int transactionID = effectiveTransactionID++;
|
||||
if (effectiveTransactionID > maxTransactionID) {
|
||||
// strictly speaking, this is not quite safe,
|
||||
// need revisit : if we have a pending
|
||||
// transaction after 64K of subsequent calls
|
||||
effectiveTransactionID = firstTransactionID;
|
||||
}
|
||||
return (short)transactionID;
|
||||
}
|
||||
|
||||
/*
|
||||
* Frees transaction ID.
|
||||
*
|
||||
* @param transactionID the ID to free.
|
||||
*/
|
||||
public static synchronized void freeTransactionID(short transactionID) {
|
||||
// empty in this implementation
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
import javax.bluetooth.DataElement;
|
||||
|
||||
/*
|
||||
* Common interface for SDP server responses listeners. Listenners are assigned
|
||||
* to active transactions and recieve SDP server responses.
|
||||
*
|
||||
*/
|
||||
public interface SDPResponseListener {
|
||||
/* Transaction cancelling reason: abnormal IO error. */
|
||||
static final int IO_ERROR = 0x010000;
|
||||
|
||||
/* Transaction cancelling reason: transaction has been terminated. */
|
||||
static final int TERMINATED = 0x010001;
|
||||
|
||||
/* Error code returned by SDP server: Invalid/unsupported SDP version. */
|
||||
static final int SDP_INVALID_VERSION = 0x01;
|
||||
|
||||
/* Error code returned by SDP server: Invalid Service Record Handle. */
|
||||
static final int SDP_INVALID_SR_HANDLE = 0x02;
|
||||
|
||||
/* Error code returned by SDP server: Invalid request syntax. */
|
||||
static final int SDP_INVALID_SYNTAX = 0x03;
|
||||
|
||||
/* Error code returned by SDP server: Invalid PDU Size. */
|
||||
static final int SDP_INVALID_PDU_SIZE = 0x04;
|
||||
|
||||
/* Error code returned by SDP server: Invalid Continuation State. */
|
||||
static final int SDP_INVALID_CONTINUATION_STATE = 0x05;
|
||||
|
||||
/*
|
||||
* Error code returned by SDP server: Insufficient Resources to satisfy
|
||||
* Request. */
|
||||
static final int SDP_INSUFFICIENT_RESOURCES = 0x06;
|
||||
|
||||
/*
|
||||
* Informs this listener about errors during Service Discovering process.
|
||||
*
|
||||
* @param errorCode error code recieved from server or generated locally
|
||||
* due to transaction terminating.
|
||||
*
|
||||
* @param info detail information about the error
|
||||
*
|
||||
* @param transactionID ID of transaction response recieved within.
|
||||
*/
|
||||
void errorResponse(int errorCode, String info, int transactionID);
|
||||
|
||||
/*
|
||||
* Informs this listener about found services records.
|
||||
*
|
||||
* @param handleList service records handles returned by server within
|
||||
* SDP_ServiceSearchResponse.
|
||||
*
|
||||
* @param transactionID ID of transaction response recieved within.
|
||||
*/
|
||||
void serviceSearchResponse(int[] handleList, int transactionID);
|
||||
|
||||
/*
|
||||
* Informs this listener about found attributes of specified service record.
|
||||
*
|
||||
* @param attrIDs list of attributes whose values requested from server
|
||||
* within SDP_ServiceAttributesRequest.
|
||||
*
|
||||
* @param attributeValues values returned by server within
|
||||
* SDP_ServiceAttributesResponse.
|
||||
*
|
||||
* @param transactionID ID of transaction response recieved within.
|
||||
*/
|
||||
void serviceAttributeResponse(int[] attrIDs,
|
||||
DataElement[] attributeValues, int transactionID);
|
||||
|
||||
/*
|
||||
* Informs this listener about attributes of fisrt found service record.
|
||||
*
|
||||
* @param attrIDs list of attributes whose values requested from server
|
||||
* within SDP_ServiceSearchAttributesRequest.
|
||||
*
|
||||
* @param attributeValues values returned by server within
|
||||
* SDP_ServiceSearchAttributesResponse.
|
||||
*
|
||||
* @param transactionID ID of transaction response recieved within.
|
||||
*/
|
||||
void serviceSearchAttributeResponse(int[] attrIDs,
|
||||
DataElement[] attributeValues, int transactionID);
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
import javax.bluetooth.BluetoothStateException;
|
||||
import javax.bluetooth.DeviceClass;
|
||||
import javax.bluetooth.DiscoveryAgent;
|
||||
import javax.bluetooth.DiscoveryListener;
|
||||
import javax.bluetooth.RemoteDevice;
|
||||
import javax.bluetooth.ServiceRecord;
|
||||
import javax.bluetooth.UUID;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
|
||||
/*
|
||||
* This class represents the module which is used by
|
||||
* DiscoveryAgent#selectService method implementation.
|
||||
*
|
||||
*/
|
||||
final class SelectServiceHandler implements DiscoveryListener {
|
||||
|
||||
/* Set to false in RR version - then the javac skip the code. */
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private Vector btDevs;
|
||||
private Hashtable btDevsHash;
|
||||
private Object btDevsLock = new Object();
|
||||
private boolean selectDevDisStarted;
|
||||
private boolean selectDevDisStopped;
|
||||
private DiscoveryAgentImpl agent;
|
||||
|
||||
/*
|
||||
* Constructs <code>SelectServiceHandler</code> for
|
||||
* <code>DiscoveryAgentImpl</code> given.
|
||||
*
|
||||
* @param agent the discovery agent to create instance for.
|
||||
*/
|
||||
SelectServiceHandler(DiscoveryAgentImpl agent) {
|
||||
this.agent = agent;
|
||||
}
|
||||
|
||||
public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
|
||||
|
||||
// if this bloototh device was found in preknown or
|
||||
// cached devices skips it now.
|
||||
if (btDevsHash.put(btDevice, btDevice) == null) {
|
||||
btDevs.addElement(btDevice);
|
||||
}
|
||||
}
|
||||
|
||||
public void inquiryCompleted(int discType) {
|
||||
synchronized (btDevsLock) {
|
||||
selectDevDisStopped = true;
|
||||
btDevsLock.notify();
|
||||
}
|
||||
}
|
||||
|
||||
public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {
|
||||
throw new RuntimeException("unexpected call");
|
||||
}
|
||||
|
||||
public void serviceSearchCompleted(int transID, int respCode) {
|
||||
throw new RuntimeException("unexpected call");
|
||||
}
|
||||
|
||||
String selectService(UUID uuid, int security, boolean master)
|
||||
throws BluetoothStateException {
|
||||
if (DEBUG) {
|
||||
System.out.println("selectService:");
|
||||
System.out.println("\tuuid=" + uuid);
|
||||
}
|
||||
Vector disDevsVector = null;
|
||||
Hashtable disDevsHash = new Hashtable();
|
||||
|
||||
if (uuid == null) {
|
||||
throw new NullPointerException("uuid is null");
|
||||
}
|
||||
|
||||
// check in CACHED and PREKNOWN devices
|
||||
String url = selectFromDevicesList(agent.retrieveDevices(
|
||||
DiscoveryAgent.PREKNOWN), uuid, security, master, disDevsHash);
|
||||
|
||||
if (url != null) {
|
||||
return url;
|
||||
}
|
||||
url = selectFromDevicesList(agent.retrieveDevices(
|
||||
DiscoveryAgent.CACHED), uuid, security, master, disDevsHash);
|
||||
|
||||
if (url != null) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// start own device discovery now
|
||||
synchronized (btDevsLock) {
|
||||
if (selectDevDisStarted) {
|
||||
throw new BluetoothStateException(
|
||||
"The previous device discovery is running...");
|
||||
}
|
||||
selectDevDisStarted = true;
|
||||
btDevs = new Vector();
|
||||
btDevsHash = disDevsHash;
|
||||
}
|
||||
|
||||
try {
|
||||
agent.startInquiry(DiscoveryAgent.GIAC, this);
|
||||
} catch (BluetoothStateException btse) {
|
||||
synchronized (btDevsLock) {
|
||||
selectDevDisStarted = false;
|
||||
btDevs = null;
|
||||
btDevsHash = null;
|
||||
}
|
||||
throw btse;
|
||||
}
|
||||
|
||||
synchronized (btDevsLock) {
|
||||
if (!selectDevDisStopped) {
|
||||
try {
|
||||
btDevsLock.wait();
|
||||
} catch (InterruptedException ie) {
|
||||
// ignore (breake waiting)
|
||||
}
|
||||
disDevsVector = btDevs;
|
||||
btDevs = null;
|
||||
btDevsHash = null;
|
||||
selectDevDisStarted = false;
|
||||
selectDevDisStopped = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < disDevsVector.size(); i++) {
|
||||
RemoteDevice btDev = (RemoteDevice) disDevsVector.elementAt(i);
|
||||
url = selectService(btDev, uuid, security, master);
|
||||
|
||||
if (url != null) {
|
||||
if (DEBUG) {
|
||||
System.out.println("\turl=" + url);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
}
|
||||
if (DEBUG) {
|
||||
System.out.println("\turl=null");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String selectFromDevicesList(RemoteDevice[] devs, UUID uuid,
|
||||
int security, boolean master, Hashtable disDevsHash) {
|
||||
if (devs == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < devs.length; i++) {
|
||||
if (disDevsHash.put(devs[i], devs[i]) != null) {
|
||||
continue;
|
||||
}
|
||||
String url = selectService(devs[i], uuid, security, master);
|
||||
|
||||
if (url != null) {
|
||||
if (DEBUG) {
|
||||
System.out.println("\turl=" + url);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String selectService(RemoteDevice btDev, UUID uuid, int security,
|
||||
boolean master) {
|
||||
UUID[] uuidSet = new UUID[] {uuid};
|
||||
ServiceSelector selector = new ServiceSelector(null, uuidSet, btDev);
|
||||
ServiceRecord serRec = selector.getServiceRecord();
|
||||
|
||||
if (serRec == null) {
|
||||
return null;
|
||||
} else {
|
||||
return serRec.getConnectionURL(security, master);
|
||||
}
|
||||
}
|
||||
} // end of class 'SelectServiceHandler' definition
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import javax.bluetooth.BluetoothStateException;
|
||||
import javax.bluetooth.DiscoveryListener;
|
||||
import javax.bluetooth.RemoteDevice;
|
||||
import javax.bluetooth.UUID;
|
||||
|
||||
public interface ServiceDiscoverer {
|
||||
/*
|
||||
* Start searching services under the given conditions
|
||||
*
|
||||
* @param attrSet list of attributes whose values are requested.
|
||||
* @param uuidSet list of UUIDs that indicate services relevant to request.
|
||||
* @param btDev remote Bluetooth device to listen response from.
|
||||
* @param discListener discovery listener.
|
||||
* @throws BluetoothStateException
|
||||
*/
|
||||
public int searchService(int[] attrSet, UUID[] uuidSet, RemoteDevice btDev,
|
||||
DiscoveryListener discListener) throws BluetoothStateException ;
|
||||
|
||||
/*
|
||||
* Cancels service discovering
|
||||
*
|
||||
* @param transID ID of a transaction to be canceled
|
||||
* @return true if transaction canceled
|
||||
*/
|
||||
public boolean cancel(int transID);
|
||||
|
||||
/*
|
||||
* Returns an <code>SDPClient<code> object and opens SDP connection
|
||||
* to the remote device with the specified Bluetooth address.
|
||||
*
|
||||
* @param bluetoothAddress bluetooth address of SDP server
|
||||
*/
|
||||
public SDPClient getSDPClient(String bluetoothAddress);
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import com.sun.j2me.main.Configuration;
|
||||
|
||||
class ServiceDiscovererFactory {
|
||||
|
||||
private static class ServiceDiscovererHolder {
|
||||
private final static ServiceDiscoverer INSTANCE
|
||||
= createServiceDiscoverer();
|
||||
}
|
||||
|
||||
/*
|
||||
* Prevents from creating instances of the factory
|
||||
*/
|
||||
private ServiceDiscovererFactory() {
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns an instance of ServiceDiscoverer
|
||||
*
|
||||
* @return ServiceDiscoverer instance
|
||||
*/
|
||||
static ServiceDiscoverer getServiceDiscoverer() {
|
||||
return ServiceDiscovererHolder.INSTANCE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates an instance of ServiceDiscoverer
|
||||
*/
|
||||
private static ServiceDiscoverer createServiceDiscoverer() {
|
||||
ServiceDiscoverer discoverer = null;
|
||||
|
||||
String serviceDiscovererName = Configuration.getProperty(
|
||||
"com.sun.jsr082.bluetooth.ServiceDiscoverer");
|
||||
if (serviceDiscovererName == null) {
|
||||
serviceDiscovererName = System.getProperty("com.sun.jsr082.bluetooth.ServiceDiscoverer");
|
||||
}
|
||||
|
||||
if (serviceDiscovererName != null) {
|
||||
try {
|
||||
Class discClass = Class.forName(serviceDiscovererName);
|
||||
discoverer = (ServiceDiscoverer)discClass.newInstance();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// #ifdef USE_NATIVE_SDDB
|
||||
return discoverer == null ? new NativeSDPClient() : discoverer;
|
||||
// #else
|
||||
return discoverer == null ? new ServiceSearcher() : discoverer;
|
||||
// #endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,643 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
import javax.bluetooth.BluetoothStateException;
|
||||
import javax.bluetooth.DataElement;
|
||||
import javax.bluetooth.LocalDevice;
|
||||
import javax.bluetooth.RemoteDevice;
|
||||
import javax.bluetooth.ServiceRecord;
|
||||
import javax.bluetooth.UUID;
|
||||
|
||||
import com.sun.jsr082.bluetooth.SDPClient;
|
||||
|
||||
/*
|
||||
* Service record implementation.
|
||||
*/
|
||||
public final class ServiceRecordImpl implements ServiceRecord {
|
||||
|
||||
/* Maxumum quantity of attributes in one request */
|
||||
static final int RETRIEVABLE_MAX;
|
||||
|
||||
/*
|
||||
* Maximum number of concurrent service searches that can
|
||||
* exist at any one time.
|
||||
*/
|
||||
static final int TRANS_MAX;
|
||||
|
||||
/* Remote device service provided by. */
|
||||
private RemoteDevice remoteDevice = null;
|
||||
|
||||
/* Service notifier. */
|
||||
private BluetoothNotifier notifier = null;
|
||||
|
||||
/* Attribues of the record. */
|
||||
private Hashtable attributesTable = null;
|
||||
|
||||
/* Bit scale that keeps service classes. */
|
||||
private int serviceClasses = 0;
|
||||
|
||||
/* Mask to identify attribute IDs out of range. */
|
||||
private static final int MASK_OVERFLOW = 0xffff0000;
|
||||
|
||||
/* Mask of incorrect class bits. */
|
||||
private static final int MASK_INCORRECT_CLASS = 0xff003fff;
|
||||
|
||||
/* ServiceRecordHandle attribute ID. */
|
||||
public static final int SERVICE_RECORD_HANDLE = 0x0000;
|
||||
|
||||
/* ProtocolDescriptorList attribute ID. */
|
||||
public static final int PROTOCOL_DESCRIPTOR_LIST = 0x0004;
|
||||
|
||||
/* Service class attribute id. */
|
||||
public static final int SERVICE_CLASS_ATTR_ID = 0x0001;
|
||||
|
||||
/* Name attribute id. */
|
||||
public static final int NAME_ATTR_ID = 0x0100;
|
||||
|
||||
/* Protocol type. */
|
||||
private int protocol = BluetoothUrl.UNKNOWN;
|
||||
|
||||
/* Bluetooth address of device service record came from. */
|
||||
private String btaddr = null;
|
||||
|
||||
/* PSM or channel id. */
|
||||
private int port = -1;
|
||||
|
||||
/* Record handle */
|
||||
private int recHandle = 0;
|
||||
/* SDPClient from where this ServiceRecord is created */
|
||||
public SDPClient sdpClient = null;
|
||||
|
||||
static {
|
||||
int retrievableMax = 5; // default value
|
||||
try {
|
||||
retrievableMax = Integer.parseInt(LocalDevice.getProperty(
|
||||
"bluetooth.sd.attr.retrievable.max"));
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.println("Internal error: ServiceRecordImpl: "
|
||||
+ "improper retrievable.max value");
|
||||
}
|
||||
RETRIEVABLE_MAX = retrievableMax;
|
||||
|
||||
int transMax = 10; // default value
|
||||
try {
|
||||
transMax = Integer.parseInt(LocalDevice.getProperty(
|
||||
"bluetooth.sd.trans.max"));
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.println("Internal error: ServiceRecordImpl: "
|
||||
+ "improper trans.max value");
|
||||
}
|
||||
TRANS_MAX = transMax;
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates service records on client device.
|
||||
*
|
||||
* @param device server device
|
||||
* @param attrIDs attributes IDs
|
||||
* @param attrValues attributes values
|
||||
*/
|
||||
public ServiceRecordImpl(RemoteDevice device, int[] attrIDs,
|
||||
DataElement[] attrValues) {
|
||||
init(attrIDs, attrValues);
|
||||
remoteDevice = device;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Creates service records for the given notifier.
|
||||
*
|
||||
* @param notifier notifier to be associated with this service record
|
||||
* @param attrIDs attributes IDs
|
||||
* @param attrValues attributes values
|
||||
*/
|
||||
public ServiceRecordImpl(BluetoothNotifier notifier, int[] attrIDs,
|
||||
DataElement[] attrValues) {
|
||||
init(attrIDs, attrValues);
|
||||
this.notifier = notifier;
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a copy of this record. The copy recieves new instances of
|
||||
* attributes values which are of types <code>DataElement.DATSEQ</code>
|
||||
* or <code>DataElement.DATALT</code> (the only data element types that
|
||||
* can be modified after creation).
|
||||
*
|
||||
* @return new instance, a copy of this one.
|
||||
*/
|
||||
public synchronized ServiceRecordImpl copy() {
|
||||
int count = attributesTable.size();
|
||||
int[] attrIDs = new int[count];
|
||||
DataElement[] attrValues = new DataElement[count];
|
||||
|
||||
Enumeration ids = attributesTable.keys();
|
||||
Enumeration values = attributesTable.elements();
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
attrIDs[i] = ((Integer)ids.nextElement()).intValue();
|
||||
// no nedd to copy elements here; service record constructor
|
||||
// performs the copying
|
||||
attrValues[i] = (DataElement)values.nextElement();
|
||||
}
|
||||
|
||||
ServiceRecordImpl servRec = new ServiceRecordImpl(notifier,
|
||||
attrIDs, attrValues);
|
||||
servRec.serviceClasses = serviceClasses;
|
||||
return servRec;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns service record handle.
|
||||
*
|
||||
* @return service record handle, or 0 if the record is not in SDDB.
|
||||
*/
|
||||
public int getHandle() {
|
||||
DataElement handle = getAttributeValue(SERVICE_RECORD_HANDLE);
|
||||
return handle != null ? (int)handle.getLong() : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets service record handle.
|
||||
*
|
||||
* @param handle new service record handle value
|
||||
*/
|
||||
public void setHandle(int handle) {
|
||||
Integer attrID = new Integer(SERVICE_RECORD_HANDLE);
|
||||
attributesTable.remove(attrID);
|
||||
attributesTable.put(attrID, new DataElement(
|
||||
DataElement.U_INT_4, handle));
|
||||
recHandle = handle;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns notifier that has created this record.
|
||||
* @return corresponding notifier.
|
||||
*/
|
||||
public BluetoothNotifier getNotifier() {
|
||||
return notifier;
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates attributes table and fills it up by values given.
|
||||
* @param attrIDs attributes IDs
|
||||
* @param attrValues attributes values
|
||||
*/
|
||||
private void init(int[] attrIDs, DataElement[] attrValues) {
|
||||
attributesTable = new Hashtable(attrIDs.length + 1);
|
||||
attrsInit(attrIDs, attrValues);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills up attributes table by values given.
|
||||
* @param attrIDs attributes IDs
|
||||
* @param attrValues attributes values
|
||||
*/
|
||||
private void attrsInit(int[] attrIDs, DataElement[] attrValues) {
|
||||
for (int i = 0; i < attrIDs.length; i++) {
|
||||
attributesTable.put(new Integer(attrIDs[i]),
|
||||
dataElementCopy(attrValues[i]));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a copy of DataElement if it's necessary.
|
||||
* @param original data element to be copied if its type
|
||||
* allows value modification
|
||||
* @return copy of data element
|
||||
*/
|
||||
private DataElement dataElementCopy(DataElement original) {
|
||||
if ((original.getDataType() == DataElement.DATSEQ)
|
||||
|| (original.getDataType() == DataElement.DATALT)) {
|
||||
DataElement copy = new DataElement(original.getDataType());
|
||||
Enumeration elements = (Enumeration) original.getValue();
|
||||
|
||||
while (elements.hasMoreElements()) {
|
||||
copy.addElement(dataElementCopy((DataElement)
|
||||
elements.nextElement()));
|
||||
}
|
||||
return copy;
|
||||
} else {
|
||||
return original;
|
||||
}
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public DataElement getAttributeValue(int attrID) {
|
||||
if ((attrID & MASK_OVERFLOW) != 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"attrID isn't a 16-bit unsigned integer");
|
||||
}
|
||||
DataElement attrValue = (DataElement) attributesTable.get(new
|
||||
Integer(attrID));
|
||||
|
||||
if (attrValue == null) {
|
||||
return null;
|
||||
} else {
|
||||
return dataElementCopy(attrValue);
|
||||
}
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public RemoteDevice getHostDevice() {
|
||||
return remoteDevice;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public synchronized int[] getAttributeIDs() {
|
||||
int[] attrIDs = new int[attributesTable.size()];
|
||||
Enumeration e = attributesTable.keys();
|
||||
|
||||
for (int i = 0; i < attrIDs.length; i++) {
|
||||
attrIDs[i] = ((Integer) e.nextElement()).intValue();
|
||||
}
|
||||
return attrIDs;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public synchronized boolean populateRecord(int[] attrIDs)
|
||||
throws IOException {
|
||||
Hashtable dupChecker = new Hashtable();
|
||||
Object checkObj = new Object();
|
||||
|
||||
if (remoteDevice == null) {
|
||||
throw new RuntimeException("local ServiceRecord");
|
||||
}
|
||||
|
||||
if (attrIDs.length == 0) {
|
||||
throw new IllegalArgumentException("attrIDs size is zero");
|
||||
}
|
||||
|
||||
if (attrIDs.length > RETRIEVABLE_MAX) {
|
||||
throw new IllegalArgumentException(
|
||||
"attrIDs size exceeds retrievable.max");
|
||||
}
|
||||
|
||||
for (int i = 0; i < attrIDs.length; i++) {
|
||||
if ((attrIDs[i] & MASK_OVERFLOW) != 0) {
|
||||
throw new IllegalArgumentException("attrID does not represent "
|
||||
+ "a 16-bit unsigned integer");
|
||||
}
|
||||
|
||||
// check attribute ID duplication
|
||||
if (dupChecker.put(new Integer(attrIDs[i]), checkObj) != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"duplicated attribute ID");
|
||||
}
|
||||
}
|
||||
|
||||
// obtains transaction ID for request
|
||||
short transactionID = SDPClientTransactionBase.newTransactionID();
|
||||
|
||||
// SDP connection and listener. They are initialized in try blok.
|
||||
SDPClient sdp = null;
|
||||
SRSDPListener listener = null;
|
||||
|
||||
try {
|
||||
// prepare data for request
|
||||
DataElement handleEl = (DataElement) attributesTable.get(
|
||||
new Integer(SERVICE_RECORD_HANDLE));
|
||||
int handle = (int) handleEl.getLong();
|
||||
|
||||
// create and prepare SDP listner
|
||||
listener = new SRSDPListener();
|
||||
|
||||
// create SDP connection and ..
|
||||
if (sdpClient == null) {
|
||||
sdp = ServiceDiscovererFactory.getServiceDiscoverer().
|
||||
getSDPClient(remoteDevice.getBluetoothAddress());
|
||||
} else {
|
||||
sdp = sdpClient;
|
||||
}
|
||||
|
||||
// ... and make request
|
||||
sdp.serviceAttributeRequest(handle, attrIDs, transactionID,
|
||||
listener);
|
||||
|
||||
synchronized (listener) {
|
||||
if ((listener.ioExcpt == null)
|
||||
&& (listener.attrValues == null)) {
|
||||
try {
|
||||
listener.wait();
|
||||
} catch (InterruptedException ie) {
|
||||
// ignore (breake waiting)
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
|
||||
// Closes SDP connection and frees transaction ID in any case
|
||||
SDPClientTransactionBase.freeTransactionID(transactionID);
|
||||
|
||||
// if connection was created try to close it
|
||||
if (sdp != null) {
|
||||
try {
|
||||
sdp.close();
|
||||
} catch (IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (listener.ioExcpt != null) {
|
||||
throw listener.ioExcpt;
|
||||
} else if (listener.attrValues == null) {
|
||||
return false;
|
||||
} else if (listener.attrValues.length == 0) {
|
||||
return false;
|
||||
} else {
|
||||
attrsInit(listener.attrIDs, listener.attrValues);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public synchronized String getConnectionURL(int requiredSecurity,
|
||||
boolean mustBeMaster) {
|
||||
// protocol, btaddr, port
|
||||
retrieveUrlCommonParams();
|
||||
if (protocol == BluetoothUrl.UNKNOWN) {
|
||||
return null;
|
||||
}
|
||||
BluetoothUrl url = BluetoothUrl.createClientUrl(
|
||||
protocol, btaddr, port);
|
||||
|
||||
if (mustBeMaster) {
|
||||
url.master = true;
|
||||
} else {
|
||||
url.master = false;
|
||||
}
|
||||
|
||||
switch (requiredSecurity) {
|
||||
case NOAUTHENTICATE_NOENCRYPT:
|
||||
break;
|
||||
case AUTHENTICATE_ENCRYPT:
|
||||
url.encrypt = true;
|
||||
case AUTHENTICATE_NOENCRYPT:
|
||||
url.authenticate = true;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("unsupported security type: "
|
||||
+ requiredSecurity);
|
||||
}
|
||||
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves service protocol, device address and port (PSM or channel)
|
||||
* from service record attributes. Results are set to
|
||||
* <code>protocol</code>, <code>btaddr</code> and <code>port</code>
|
||||
* variables correspondingly.
|
||||
*/
|
||||
private void retrieveUrlCommonParams() {
|
||||
if (protocol != BluetoothUrl.UNKNOWN) {
|
||||
// already retrieved
|
||||
return;
|
||||
}
|
||||
|
||||
if (remoteDevice != null) {
|
||||
btaddr = remoteDevice.getBluetoothAddress();
|
||||
} else {
|
||||
try {
|
||||
btaddr = LocalDevice.getLocalDevice().getBluetoothAddress();
|
||||
} catch (BluetoothStateException bse) {
|
||||
throw new IllegalArgumentException("cannot generate url");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* There are three protocols supported -
|
||||
* they are obex or rfcomm or l2cap. So, if obex is
|
||||
* found in ProtocolDescriptorList, the protocol is btgoep,
|
||||
* if RFCOMM is found (and no obex) - the btspp, otherwise
|
||||
* the protocol is btl2cap.
|
||||
*/
|
||||
DataElement protocolList = getAttributeValue(PROTOCOL_DESCRIPTOR_LIST);
|
||||
if (protocolList == null) {
|
||||
return;
|
||||
}
|
||||
Enumeration val = (Enumeration) protocolList.getValue();
|
||||
int type = -1; // 0 = l2cap, 1 = spp, 2 = obex
|
||||
final UUID L2CAP_UUID = new UUID(0x0100);
|
||||
final UUID RFCOMM_UUID = new UUID(0x0003);
|
||||
final UUID OBEX_UUID = new UUID(0x0008);
|
||||
|
||||
// go through all of the protocols in the protocols list
|
||||
while (val.hasMoreElements()) {
|
||||
DataElement protoDE = (DataElement) val.nextElement();
|
||||
|
||||
// application adds a garbage in protocolList - ignore
|
||||
if (protoDE.getDataType() != DataElement.DATSEQ) {
|
||||
continue;
|
||||
}
|
||||
Enumeration protoEnum = (Enumeration) protoDE.getValue();
|
||||
int tmpPort = -1;
|
||||
int tmpType = -1;
|
||||
|
||||
// look on protocol details
|
||||
while (protoEnum.hasMoreElements()) {
|
||||
DataElement de = (DataElement) protoEnum.nextElement();
|
||||
|
||||
// may be PSM or channel id
|
||||
if (de.getDataType() == DataElement.U_INT_1 ||
|
||||
de.getDataType() == DataElement.U_INT_2) {
|
||||
tmpPort = (int) de.getLong();
|
||||
} else if (de.getDataType() == DataElement.UUID) {
|
||||
UUID protoUUID = (UUID) de.getValue();
|
||||
|
||||
if (protoUUID.equals(L2CAP_UUID)) {
|
||||
tmpType = 0;
|
||||
} else if (protoUUID.equals(RFCOMM_UUID)) {
|
||||
tmpType = 1;
|
||||
} else if (protoUUID.equals(OBEX_UUID)) {
|
||||
tmpType = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ok, new protocol has been parsed - let's check if it
|
||||
* is over the previous one or not.
|
||||
*
|
||||
* Note, that OBEX protocol may appear before the RFCOMM
|
||||
* one - in this case the port (channel id) is not set -
|
||||
* need to check this case separately.
|
||||
*/
|
||||
if (tmpType > type) {
|
||||
type = tmpType;
|
||||
|
||||
// no "port" for obex type (obex = 2)
|
||||
if (tmpType != 2) {
|
||||
port = tmpPort;
|
||||
}
|
||||
} else if (tmpType == 1) {
|
||||
port = tmpPort;
|
||||
}
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 0:
|
||||
protocol = BluetoothUrl.L2CAP;
|
||||
break;
|
||||
case 1:
|
||||
protocol = BluetoothUrl.RFCOMM;
|
||||
break;
|
||||
case 2:
|
||||
protocol = BluetoothUrl.OBEX;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("wrong protocol list");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve service classes bits provided by corresponing service
|
||||
* at local device.
|
||||
*
|
||||
* @return an integer that keeps the service classes bits
|
||||
*/
|
||||
public int getDeviceServiceClasses() {
|
||||
if (remoteDevice != null) {
|
||||
throw new RuntimeException(
|
||||
"This ServiceRecord was created by a call to "
|
||||
+ "DiscoveryAgent.searchServices()");
|
||||
}
|
||||
|
||||
// it's necessary to improve these code
|
||||
return serviceClasses;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public synchronized void setDeviceServiceClasses(int classes) {
|
||||
// checks that it's service record from remote device
|
||||
if (remoteDevice != null) {
|
||||
throw new RuntimeException("This ServiceRecord was created"
|
||||
+ " by a call to DiscoveryAgent.searchServices()");
|
||||
}
|
||||
|
||||
// checks correction of set classbits
|
||||
if ((classes & MASK_INCORRECT_CLASS) != 0) {
|
||||
throw new IllegalArgumentException("attempt to set incorrect bits");
|
||||
}
|
||||
serviceClasses = classes;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public synchronized boolean setAttributeValue(
|
||||
int attrID, DataElement attrValue) {
|
||||
|
||||
if ((attrID & MASK_OVERFLOW) != 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"attrID does not represent a 16-bit unsigned integer");
|
||||
}
|
||||
|
||||
if (attrID == SERVICE_RECORD_HANDLE) {
|
||||
throw new IllegalArgumentException(
|
||||
"attrID is the value of ServiceRecordHandle (0x0000)");
|
||||
}
|
||||
|
||||
if (remoteDevice != null) {
|
||||
throw new RuntimeException(
|
||||
"can't update ServiceRecord of the RemoteDevice");
|
||||
}
|
||||
Object key = new Integer(attrID);
|
||||
|
||||
if (attrValue == null) {
|
||||
return attributesTable.remove(key) != null;
|
||||
} else {
|
||||
attributesTable.put(key, dataElementCopy(attrValue));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* SDP responce listener that is used within <code>populateRecord()</code>
|
||||
* processing.
|
||||
*/
|
||||
class SRSDPListener implements SDPResponseListener {
|
||||
/* Attributes values retrieved form remote device. */
|
||||
DataElement[] attrValues = null;
|
||||
/* Keeps an IOException to be thrown. */
|
||||
IOException ioExcpt = null;
|
||||
/* IDs of attributes to be retrieved. */
|
||||
int[] attrIDs = null;
|
||||
|
||||
/*
|
||||
* Receives error response.
|
||||
* @param errorCode error code
|
||||
* @param info error information
|
||||
* @param transactionID transaction ID
|
||||
*/
|
||||
public void errorResponse(int errorCode, String info,
|
||||
int transactionID) {
|
||||
synchronized (this) {
|
||||
ioExcpt = new IOException(info);
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements required SDPResponseListener method,
|
||||
* must not be called.
|
||||
* @param handleList no matter
|
||||
* @param transactionID no matter
|
||||
*/
|
||||
public void serviceSearchResponse(int[] handleList,
|
||||
int transactionID) {
|
||||
throw new RuntimeException("unexpected call");
|
||||
}
|
||||
|
||||
/*
|
||||
* Receives arrays of service record attributes and their values.
|
||||
* @param attributeIDs list of attributes whose values were requested
|
||||
* from server.
|
||||
* @param attributeValues values returned by server within.
|
||||
* @param transactionID ID of transaction response recieved within.
|
||||
*/
|
||||
public void serviceAttributeResponse(int[] attributeIDs,
|
||||
DataElement[] attributeValues, int transactionID) {
|
||||
synchronized (this) {
|
||||
attrIDs = attributeIDs;
|
||||
attrValues = attributeValues;
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements required SDPResponseListener method,
|
||||
* must not be called.
|
||||
* @param attrIDs no matter
|
||||
* @param attributeValues no matter
|
||||
* @param transactionID no matter
|
||||
*/
|
||||
public void serviceSearchAttributeResponse(int[] attrIDs,
|
||||
DataElement[] attributeValues, int transactionID) {
|
||||
throw new RuntimeException("unexpected call");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import javax.bluetooth.DataElement;
|
||||
import javax.bluetooth.RemoteDevice;
|
||||
import javax.bluetooth.ServiceRecord;
|
||||
|
||||
/*
|
||||
* Serializes ServiceRecord objects.
|
||||
*/
|
||||
public class ServiceRecordSerializer {
|
||||
|
||||
/* Helper object used for data element serialization. */
|
||||
static DataElementSerializer des;
|
||||
|
||||
/* Initializes static fields. */
|
||||
static {
|
||||
des = new DataElementSerializer();
|
||||
}
|
||||
|
||||
/*
|
||||
* Serializes given service record - creates an array of bytes representing
|
||||
* data elements as described in Bluetooth Specification Version 1.2,
|
||||
* vol 3, page 127.
|
||||
*
|
||||
* @param record the service record to serialize
|
||||
* @return an array containing the serialized record
|
||||
*/
|
||||
public static synchronized byte[] serialize(ServiceRecord record) {
|
||||
DataElement seq = new DataElement(DataElement.DATSEQ);
|
||||
int[] attrIDs = record.getAttributeIDs();
|
||||
for (int i = 0; i < attrIDs.length; i++) {
|
||||
DataElement attrID = new DataElement(DataElement.U_INT_2,
|
||||
attrIDs[i]);
|
||||
DataElement attrValue = record.getAttributeValue(attrIDs[i]);
|
||||
if (attrValue != null) {
|
||||
seq.addElement(attrID);
|
||||
seq.addElement(attrValue);
|
||||
}
|
||||
}
|
||||
try {
|
||||
return des.serialize(seq);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Restores previously serialized service record.
|
||||
*
|
||||
* @param notifier notifier object the newly created record
|
||||
* to be associated with
|
||||
* @param data serialized service record data
|
||||
* @return restored service record
|
||||
*/
|
||||
public static synchronized ServiceRecordImpl restore(
|
||||
BluetoothNotifier notifier, byte[] data) {
|
||||
DataElement seq;
|
||||
try {
|
||||
seq = des.restore(data);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
Enumeration elements = (Enumeration)seq.getValue();
|
||||
int[] attrIDs = new int[seq.getSize() / 2];
|
||||
DataElement[] attrValues = new DataElement[attrIDs.length];
|
||||
for (int i = 0; i < attrIDs.length; i++) {
|
||||
attrIDs[i] = (int)((DataElement)elements.nextElement()).getLong();
|
||||
DataElement attrValue = (DataElement)elements.nextElement();
|
||||
DataElement newAttrValue;
|
||||
int dataType = attrValue.getDataType();
|
||||
switch (dataType) {
|
||||
case DataElement.BOOL:
|
||||
newAttrValue = new DataElement(attrValue.getBoolean());
|
||||
break;
|
||||
case DataElement.NULL:
|
||||
newAttrValue = new DataElement(DataElement.NULL);
|
||||
break;
|
||||
case DataElement.U_INT_1:
|
||||
case DataElement.U_INT_2:
|
||||
case DataElement.U_INT_4:
|
||||
case DataElement.INT_1:
|
||||
case DataElement.INT_2:
|
||||
case DataElement.INT_4:
|
||||
case DataElement.INT_8:
|
||||
newAttrValue = new DataElement(dataType,
|
||||
attrValue.getLong());
|
||||
break;
|
||||
case DataElement.DATALT:
|
||||
case DataElement.DATSEQ:
|
||||
Enumeration e = (Enumeration)attrValue.getValue();
|
||||
newAttrValue = new DataElement(dataType);
|
||||
while (e.hasMoreElements()) {
|
||||
newAttrValue.addElement((DataElement)e.nextElement());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
newAttrValue = new DataElement(attrValue.getDataType(),
|
||||
attrValue.getValue());
|
||||
break;
|
||||
}
|
||||
attrValues[i] = newAttrValue;
|
||||
}
|
||||
return new ServiceRecordImpl(notifier, attrIDs, attrValues);
|
||||
}
|
||||
|
||||
public static synchronized ServiceRecordImpl restore(RemoteDevice device, byte[] data ) {
|
||||
DataElement seq;
|
||||
try {
|
||||
seq = des.restore(data);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
Enumeration elements = (Enumeration)seq.getValue();
|
||||
int[] attrIDs = new int[seq.getSize() / 2];
|
||||
DataElement[] attrValues = new DataElement[attrIDs.length];
|
||||
for (int i = 0; i < attrIDs.length; i++) {
|
||||
attrIDs[i] = (int)((DataElement)elements.nextElement()).getLong();
|
||||
DataElement attrValue = (DataElement)elements.nextElement();
|
||||
DataElement newAttrValue;
|
||||
int dataType = attrValue.getDataType();
|
||||
switch (dataType) {
|
||||
case DataElement.BOOL:
|
||||
newAttrValue = new DataElement(attrValue.getBoolean());
|
||||
break;
|
||||
case DataElement.NULL:
|
||||
newAttrValue = new DataElement(DataElement.NULL);
|
||||
break;
|
||||
case DataElement.U_INT_1:
|
||||
case DataElement.U_INT_2:
|
||||
case DataElement.U_INT_4:
|
||||
case DataElement.INT_1:
|
||||
case DataElement.INT_2:
|
||||
case DataElement.INT_4:
|
||||
case DataElement.INT_8:
|
||||
newAttrValue = new DataElement(dataType,
|
||||
attrValue.getLong());
|
||||
break;
|
||||
case DataElement.DATALT:
|
||||
case DataElement.DATSEQ:
|
||||
Enumeration e = (Enumeration)attrValue.getValue();
|
||||
newAttrValue = new DataElement(dataType);
|
||||
while (e.hasMoreElements()) {
|
||||
newAttrValue.addElement((DataElement)e.nextElement());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
newAttrValue = new DataElement(attrValue.getDataType(),
|
||||
attrValue.getValue());
|
||||
break;
|
||||
}
|
||||
attrValues[i] = newAttrValue;
|
||||
}
|
||||
return new ServiceRecordImpl(device, attrIDs, attrValues);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,600 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.bluetooth.BluetoothStateException;
|
||||
import javax.bluetooth.DataElement;
|
||||
import javax.bluetooth.DiscoveryListener;
|
||||
import javax.bluetooth.RemoteDevice;
|
||||
import javax.bluetooth.UUID;
|
||||
|
||||
import com.sun.jsr082.bluetooth.JavaSDPClient;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
|
||||
/*
|
||||
* This class saves information about responses to SDP_ServiceSearchRequest and
|
||||
* SDP_ServiceAttributeRequest requests and provides functionality of
|
||||
* DiscoveryAgent.serviceSearch using multiple requests via JavaSDPClient (Service
|
||||
* Discovery Protocol)
|
||||
*
|
||||
*/
|
||||
public class ServiceSearcher extends ServiceSearcherBase implements
|
||||
ServiceDiscoverer {
|
||||
|
||||
/* Set to false in RR version - then the javac skip the code. */
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/* this class name for debug. */
|
||||
private static final String cn = "ServiceSearcher";
|
||||
|
||||
/* ID of transaction this listener works in. */
|
||||
private short transactionID;
|
||||
|
||||
/*
|
||||
* Listens for service discovery and is informed as a service is discovered.
|
||||
*/
|
||||
private DiscoveryListener discListener;
|
||||
|
||||
/* SDP client to send requests. */
|
||||
private JavaSDPClient sdp;
|
||||
|
||||
/* Service records handles retrieved from a server response. */
|
||||
private int[] handles;
|
||||
|
||||
/* Number of service records handles processed. */
|
||||
private int processedHandle;
|
||||
|
||||
/* Indicates if this listener is inactive. */
|
||||
private boolean inactive = false;
|
||||
|
||||
/* Indicates if service search has been canceled. */
|
||||
private boolean canceled = false;
|
||||
|
||||
/* Indicates if listener notification has been called. */
|
||||
private boolean notified = false;
|
||||
|
||||
/* Current quantity of service discovering requests. */
|
||||
private static int requestCounter = 0;
|
||||
|
||||
/* Keeps the references of current service search requests. */
|
||||
private static Hashtable searchers = new Hashtable();
|
||||
|
||||
/*
|
||||
* Creates ServiceSearcher and save all required info in it.
|
||||
*
|
||||
*/
|
||||
public ServiceSearcher() {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns an <code>JavaSDPClient<code> object and opens SDP connection
|
||||
* to the remote device with the specified Bluetooth address.
|
||||
*
|
||||
* @param bluetoothAddress bluetooth address of SDP server
|
||||
*/
|
||||
public SDPClient getSDPClient(String bluetoothAddress) {
|
||||
try {
|
||||
sdp = new JavaSDPClient(bluetoothAddress);
|
||||
} catch (IOException ioe) {
|
||||
|
||||
}
|
||||
return (SDPClient)sdp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start searching services under the given conditions
|
||||
*
|
||||
* @param attrSet
|
||||
* list of attributes whose values are requested.
|
||||
* @param uuidSet
|
||||
* list of UUIDs that indicate services relevant to request.
|
||||
* @param btDev
|
||||
* remote Bluetooth device to listen response from.
|
||||
* @param discListener
|
||||
* discovery listener.
|
||||
*/
|
||||
public int searchService(int[] attrSet, UUID[] uuidSet, RemoteDevice btDev,
|
||||
DiscoveryListener discListener) throws BluetoothStateException,
|
||||
IllegalArgumentException {
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("- serviceSearcher: initializing");
|
||||
}
|
||||
initialize(attrSet, uuidSet, btDev);
|
||||
|
||||
if (discListener == null) {
|
||||
throw new NullPointerException("DiscoveryListener is null");
|
||||
}
|
||||
this.discListener = discListener;
|
||||
|
||||
return start();
|
||||
}
|
||||
|
||||
/*
|
||||
* Starts SDP_ServiceSearchRequest.
|
||||
*
|
||||
* @see JavaSDPClient#serviceSearchRequest
|
||||
*
|
||||
* @return ID of transaction that has been initiated by the request.
|
||||
*/
|
||||
private int start() throws BluetoothStateException {
|
||||
if (DEBUG) {
|
||||
System.out.println("- serviceSearcher: start");
|
||||
}
|
||||
synchronized (ServiceSearcher.class) {
|
||||
if (requestCounter == ServiceRecordImpl.TRANS_MAX) {
|
||||
throw new BluetoothStateException(
|
||||
"Too much concurrent requests");
|
||||
}
|
||||
requestCounter++;
|
||||
}
|
||||
transactionID = SDPClientTransaction.newTransactionID();
|
||||
searchers.put(new Integer(transactionID), this);
|
||||
synchronized (this) {
|
||||
notified = false;
|
||||
}
|
||||
|
||||
handles = null;
|
||||
processedHandle = 0;
|
||||
try {
|
||||
sdp = new JavaSDPClient(btDev.getBluetoothAddress());
|
||||
sdp.serviceSearchAttributeRequest(attrSet, uuidSet, transactionID,
|
||||
this);
|
||||
// sdp.serviceSearchRequest(uuidSet, transactionID, this);
|
||||
} catch (IOException ioe) {
|
||||
if (DEBUG) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
stop();
|
||||
new NotifyListenerRunner(
|
||||
DiscoveryListener.SERVICE_SEARCH_DEVICE_NOT_REACHABLE);
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("- serviceSearch: started with transaction: "
|
||||
+ transactionID);
|
||||
}
|
||||
return transactionID;
|
||||
}
|
||||
|
||||
/*
|
||||
* Receives SDP_ErrorResponse and completes the search request activity by
|
||||
* error reason.
|
||||
*
|
||||
* @param errorCode
|
||||
* error code form SDP_ErrorResponse.
|
||||
* @param info
|
||||
* error details firm SDP_ErrorResponse.
|
||||
* @param transactionID
|
||||
* ID of transaction response got within.
|
||||
*/
|
||||
public void errorResponse(int errorCode, String info, int transactionID) {
|
||||
if (DEBUG) {
|
||||
System.out.println(cn + ".errorResponse: called");
|
||||
}
|
||||
|
||||
stop();
|
||||
|
||||
if ((errorCode == SDP_INVALID_VERSION)
|
||||
|| (errorCode == SDP_INVALID_SYNTAX)
|
||||
|| (errorCode == SDP_INVALID_PDU_SIZE)
|
||||
|| (errorCode == SDP_INVALID_CONTINUATION_STATE)
|
||||
|| (errorCode == SDP_INSUFFICIENT_RESOURCES)) {
|
||||
notifyListener(DiscoveryListener.SERVICE_SEARCH_ERROR);
|
||||
System.err.println(info);
|
||||
} else if (errorCode == SDP_INVALID_SR_HANDLE) {
|
||||
notifyListener(DiscoveryListener.SERVICE_SEARCH_NO_RECORDS);
|
||||
System.err.println(info);
|
||||
} else if (errorCode == IO_ERROR) {
|
||||
notifyListener(DiscoveryListener.SERVICE_SEARCH_DEVICE_NOT_REACHABLE);
|
||||
} else if (errorCode == TERMINATED) {
|
||||
new NotifyListenerRunner(
|
||||
DiscoveryListener.SERVICE_SEARCH_TERMINATED);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Receives array of handles retrieved form SDP_serviceSearchResponse.
|
||||
*
|
||||
* @param handleList
|
||||
* service record handles retrieved from
|
||||
* SDP_srviceSearchResponse.
|
||||
* @param transactionID
|
||||
* ID of transaction response has been received in.
|
||||
*/
|
||||
public void serviceSearchResponse(int[] handleList, int transactionID) {
|
||||
if (DEBUG) {
|
||||
System.out.println(cn + ".serviceSearchResponse: called");
|
||||
}
|
||||
|
||||
// there is no reason to perform response processing if search
|
||||
// is canceled
|
||||
if (isCanceled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (handleList == null || handleList.length == 0) {
|
||||
stop();
|
||||
notifyListener(DiscoveryListener.SERVICE_SEARCH_NO_RECORDS);
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
handles = handleList;
|
||||
processedHandle = 0;
|
||||
}
|
||||
|
||||
try {
|
||||
// there is no reason to request service attributes if service
|
||||
// search is canceled
|
||||
if (isCanceled()) {
|
||||
return;
|
||||
}
|
||||
sdp.serviceAttributeRequest(handles[processedHandle], attrSet,
|
||||
transactionID, this);
|
||||
} catch (IOException ioe) {
|
||||
if (DEBUG) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
stop();
|
||||
notifyListener(DiscoveryListener.SERVICE_SEARCH_DEVICE_NOT_REACHABLE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Receives arrays of service record attributes and their values retrieved
|
||||
* from server response.
|
||||
*/
|
||||
public void serviceAttributeResponse(int[] attrIDs,
|
||||
DataElement[] attributeValues, int transactionID) {
|
||||
if (DEBUG) {
|
||||
System.out.println(cn + ".serviceAttributeResponse: called");
|
||||
}
|
||||
|
||||
// there is no reason to process service attributes if service
|
||||
// search is canceled
|
||||
if (isCanceled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
processedHandle++;
|
||||
}
|
||||
|
||||
if (attributeValues != null) {
|
||||
ServiceRecordImpl[] serviceRecordSet = new ServiceRecordImpl[1];
|
||||
serviceRecordSet[0] = new ServiceRecordImpl(btDev, attrIDs,
|
||||
attributeValues);
|
||||
try {
|
||||
// The spec for DiscoveryAgent.cancelServiceSearch() says:
|
||||
// "After receiving SERVICE_SEARCH_TERMINATED event,
|
||||
// no further servicesDiscovered() events will occur
|
||||
// as a result of this search."
|
||||
if (isCanceled()) {
|
||||
return;
|
||||
}
|
||||
System.out.println("serviceSearch: notify serviceDiscovered");
|
||||
discListener.servicesDiscovered(this.transactionID,
|
||||
serviceRecordSet);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (processedHandle == handles.length) {
|
||||
stop();
|
||||
if (DEBUG) {
|
||||
System.out
|
||||
.println("serviceSearch: notify service search completed");
|
||||
}
|
||||
notifyListener(DiscoveryListener.SERVICE_SEARCH_COMPLETED);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// there is no reason to continue attributes discovery if search
|
||||
// is canceled
|
||||
if (isCanceled()) {
|
||||
return;
|
||||
}
|
||||
sdp.serviceAttributeRequest(handles[processedHandle], attrSet,
|
||||
transactionID, this);
|
||||
} catch (IOException ioe) {
|
||||
if (DEBUG) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
stop();
|
||||
notifyListener(DiscoveryListener.SERVICE_SEARCH_DEVICE_NOT_REACHABLE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Base class method not relevant to this subclass, must never be called.
|
||||
*/
|
||||
public void serviceSearchAttributeResponse(int[] attrIDs,
|
||||
DataElement[] attributeValues, int transactionID) {
|
||||
if (DEBUG) {
|
||||
System.out.println(cn + ".serviceSearchAttributeResponse: called");
|
||||
}
|
||||
// there is no reason to process service attributes if service
|
||||
// search is canceled
|
||||
if (isCanceled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (attrIDs == null || attrIDs.length <= 0 || attributeValues == null
|
||||
|| attrIDs.length != attributeValues.length) {
|
||||
notifyListener(DiscoveryListener.SERVICE_SEARCH_NO_RECORDS);
|
||||
return;
|
||||
}
|
||||
int firstPos = 0;
|
||||
Vector responceRecords = new Vector();
|
||||
for (int i = 1; i < attrIDs.length; i++) {
|
||||
if (attrIDs[i] == 0) {
|
||||
responceRecords.addElement(getOneRecord(attrIDs,
|
||||
attributeValues, firstPos, i));
|
||||
firstPos = i;
|
||||
}
|
||||
}
|
||||
responceRecords.addElement(getOneRecord(attrIDs,
|
||||
attributeValues, firstPos, attrIDs.length));
|
||||
|
||||
ServiceRecordImpl[] records = new ServiceRecordImpl[responceRecords.size()];
|
||||
for (int i=0; i<responceRecords.size(); i++) {
|
||||
records[i]=(ServiceRecordImpl)responceRecords.elementAt(i);
|
||||
}
|
||||
|
||||
try {
|
||||
// The spec for DiscoveryAgent.cancelServiceSearch() says:
|
||||
// "After receiving SERVICE_SEARCH_TERMINATED event,
|
||||
// no further servicesDiscovered() events will occur
|
||||
// as a result of this search."
|
||||
if (isCanceled()) {
|
||||
return;
|
||||
}
|
||||
if (DEBUG) {
|
||||
System.out.println("serviceSearch: notify serviceDiscovered");
|
||||
}
|
||||
discListener.servicesDiscovered(this.transactionID, records);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
stop();
|
||||
if (DEBUG) {
|
||||
System.out
|
||||
.println("serviceSearch: notify service_search_completed");
|
||||
}
|
||||
notifyListener(DiscoveryListener.SERVICE_SEARCH_COMPLETED);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns one ServiceRecord from the incoming attributes list
|
||||
*/
|
||||
private ServiceRecordImpl getOneRecord(int[] attrIDs,
|
||||
DataElement[] attributeValues, int firstPos, int curPos) {
|
||||
|
||||
int aLength = curPos - firstPos;
|
||||
int[] aIDs = new int[aLength];
|
||||
DataElement[] aValues = new DataElement[aLength];
|
||||
System.arraycopy(attrIDs, firstPos, aIDs, 0, aLength);
|
||||
System.arraycopy(attributeValues, firstPos, aValues, 0, aLength);
|
||||
ServiceRecordImpl record =
|
||||
new ServiceRecordImpl(btDev, aIDs, aValues);
|
||||
return record;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finishes the service searcher activity.
|
||||
*/
|
||||
private void stop() {
|
||||
JavaSDPClient sdp;
|
||||
searchers.remove(new Integer(transactionID));
|
||||
|
||||
synchronized (this) {
|
||||
if (this.sdp == null) {
|
||||
return;
|
||||
}
|
||||
inactive = true;
|
||||
sdp = this.sdp;
|
||||
this.sdp = null;
|
||||
}
|
||||
|
||||
synchronized (ServiceSearcher.class) {
|
||||
requestCounter--;
|
||||
}
|
||||
|
||||
try {
|
||||
sdp.close();
|
||||
} catch (IOException ioe) {
|
||||
if (DEBUG) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Cancels current transaction.
|
||||
*
|
||||
* @return false if there is no current transaction, cancels it and returns
|
||||
* true otherwise.
|
||||
*/
|
||||
boolean cancel() {
|
||||
synchronized (this) {
|
||||
if (inactive) {
|
||||
return false;
|
||||
}
|
||||
inactive = true;
|
||||
|
||||
if (sdp == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (canceled) {
|
||||
return false;
|
||||
}
|
||||
canceled = true;
|
||||
}
|
||||
|
||||
// cancel running effective transaction if any.
|
||||
// if sdp.cancelServiceSearch returns false (there is no running
|
||||
// transactions) then call the notification directly.
|
||||
if (!sdp.cancelServiceSearch(transactionID)) {
|
||||
new NotifyListenerRunner(
|
||||
DiscoveryListener.SERVICE_SEARCH_TERMINATED);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines whether the service search has been canceled by the
|
||||
* application and did not complete.
|
||||
*
|
||||
* @return <code>true</code> indicates the service search has been
|
||||
* canceled; <code>false</code> the application has not called
|
||||
* cancel operation
|
||||
*/
|
||||
private boolean isCanceled() {
|
||||
return canceled;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cancels transaction with given ID.
|
||||
*
|
||||
* @param transactionID
|
||||
* ID of transaction to be cancelled.
|
||||
*
|
||||
* @return false if there is no open transaction with ID given, true
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean cancel(int transactionID) {
|
||||
ServiceSearcher carrier = (ServiceSearcher) searchers.get(new Integer(
|
||||
transactionID));
|
||||
|
||||
if (carrier == null) {
|
||||
return false;
|
||||
} else {
|
||||
return carrier.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Notifies the listener that service search has been completed with
|
||||
* specified response code.
|
||||
*
|
||||
* @param respCode
|
||||
* response code.
|
||||
*/
|
||||
private void notifyListener(int respCode) {
|
||||
// guard against multiple notification calls
|
||||
synchronized (this) {
|
||||
if (!notified) {
|
||||
notified = true;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
String codeStr = "Undefined";
|
||||
|
||||
switch (respCode) {
|
||||
case DiscoveryListener.SERVICE_SEARCH_COMPLETED:
|
||||
codeStr = "SERVICE_SEARCH_COMPLETED";
|
||||
break;
|
||||
|
||||
case DiscoveryListener.SERVICE_SEARCH_DEVICE_NOT_REACHABLE:
|
||||
codeStr = "SERVICE_SEARCH_DEVICE_NOT_REACHABLE";
|
||||
break;
|
||||
|
||||
case DiscoveryListener.SERVICE_SEARCH_ERROR:
|
||||
codeStr = "SERVICE_SEARCH_ERROR";
|
||||
break;
|
||||
|
||||
case DiscoveryListener.SERVICE_SEARCH_NO_RECORDS:
|
||||
codeStr = "SERVICE_SEARCH_NO_RECORDS";
|
||||
break;
|
||||
|
||||
case DiscoveryListener.SERVICE_SEARCH_TERMINATED:
|
||||
codeStr = "SERVICE_SEARCH_TERMINATED";
|
||||
break;
|
||||
default:
|
||||
}
|
||||
if (DEBUG) {
|
||||
System.out.println("serviceSearchCompleted:");
|
||||
System.out.println("\ttransID=" + transactionID);
|
||||
System.out.println("\trespCode=" + codeStr);
|
||||
System.out.println("\tinactive=" + inactive);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
discListener.serviceSearchCompleted(transactionID, respCode);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Runnable for launching <code>notifyListener()</code> in a separate
|
||||
* thread.
|
||||
*
|
||||
* @see #notifyListener(int)
|
||||
*/
|
||||
class NotifyListenerRunner implements Runnable {
|
||||
/* Response code to pass to a listener. */
|
||||
int respCode;
|
||||
|
||||
/*
|
||||
* Constructs Runnable instance to pass single response code, starts a
|
||||
* thread for that.
|
||||
*
|
||||
* @param respCode
|
||||
* response code value
|
||||
*/
|
||||
NotifyListenerRunner(int respCode) {
|
||||
this.respCode = respCode;
|
||||
new Thread(this).start();
|
||||
}
|
||||
|
||||
/*
|
||||
* The <code>run()</code> method.
|
||||
*
|
||||
* @see java.lang.Runnable
|
||||
*/
|
||||
public void run() {
|
||||
notifyListener(respCode);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
import javax.bluetooth.RemoteDevice;
|
||||
import javax.bluetooth.UUID;
|
||||
import javax.bluetooth.DataElement;
|
||||
|
||||
import com.sun.jsr082.bluetooth.SDPClient;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
|
||||
/*
|
||||
* This class saves information about every service descovery request
|
||||
* to provide functionality of DiscoveryAgent using multiple requests
|
||||
* via SDPClient (Service Descovery Protocol) by ServiceSelector
|
||||
* and ServiceSearcher classes.
|
||||
*/
|
||||
abstract public class ServiceSearcherBase implements SDPResponseListener {
|
||||
|
||||
/*
|
||||
* maximum number of allowed UUIDS in search uuids sequence
|
||||
*/
|
||||
private static final int MAX_ALLOWED_UUIDS = 12;
|
||||
private static final Object FAKE_VALUE = new Object();
|
||||
/* Mask to determine an attribute ID out of range. */
|
||||
private static final int MASK_OVERFLOW = 0xffff0000;
|
||||
|
||||
/* RemoteDevice whose response to be listened. */
|
||||
RemoteDevice btDev;
|
||||
|
||||
/*
|
||||
* The UUIDs from SDP_ServiceSearchRequest or
|
||||
* SDP_ServiceSearchAttrbuteRequest.
|
||||
*
|
||||
* @see SDPClient#serviceSearchRequest
|
||||
* @see SDPClient#serviceSearchAttributeRequest
|
||||
*/
|
||||
UUID[] uuidSet;
|
||||
|
||||
/*
|
||||
* Attributes list from SDP_ServiceSearchAttrbuteRequest.
|
||||
*
|
||||
* @see SDPClient#serviceSearchAttributeRequest
|
||||
*/
|
||||
int[] attrSet;
|
||||
|
||||
/*
|
||||
* Creates ServiceSearcherBase and save all required info in it.
|
||||
*/
|
||||
ServiceSearcherBase() {
|
||||
super();
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates an instance of ServiceSearcherBase
|
||||
*
|
||||
* @param attrSet list of attributes whose values are requested.
|
||||
* @param uuidSet list of UUIDs that indicate services relevant to request.
|
||||
* @param btDev remote Bluetooth device to listen response from.
|
||||
* @param discListener discovery listener.
|
||||
*/
|
||||
ServiceSearcherBase(int[] attrSet, UUID[] uuidSet, RemoteDevice btDev) {
|
||||
super();
|
||||
initialize(attrSet, uuidSet, btDev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes an instance of ServiceSearcherBase
|
||||
*
|
||||
* @param attrSet list of attributes whose values are requested.
|
||||
* @param uuidSet list of UUIDs that indicate services relevant to request.
|
||||
* @param btDev remote Bluetooth device to listen response from.
|
||||
* @param discListener discovery listener.
|
||||
*/
|
||||
protected void initialize(int[] attrSet, UUID[] uuidSet, RemoteDevice btDev)
|
||||
throws IllegalArgumentException, NullPointerException {
|
||||
|
||||
this.btDev = btDev;
|
||||
this.attrSet = ServiceSearcherBase.extendByStandardAttrs(attrSet);
|
||||
this.uuidSet = ServiceSearcherBase.removeDuplicatedUuids(uuidSet);
|
||||
}
|
||||
|
||||
/*
|
||||
* Informs this listener about errors during Service Discovering process.
|
||||
*
|
||||
* @param errorCode error code recieved from server or generated locally
|
||||
* due to transaction terminating.
|
||||
*
|
||||
* @param info detail information about the error
|
||||
*
|
||||
* @param transactionID ID of transaction response recieved within.
|
||||
*/
|
||||
abstract public void errorResponse(int errorCode, String info,
|
||||
int transactionID);
|
||||
|
||||
/*
|
||||
* Informs this listener about found services records.
|
||||
*
|
||||
* @param handleList service records handles returned by server within
|
||||
* SDP_ServiceSearchResponse.
|
||||
*
|
||||
* @param transactionID ID of transaction response recieved within.
|
||||
*/
|
||||
abstract public void serviceSearchResponse(int[] handleList,
|
||||
int transactionID);
|
||||
|
||||
/*
|
||||
* Informs this listener about found attributes of specified service record.
|
||||
*
|
||||
* @param attrIDs list of attributes whose values requested from server
|
||||
* within SDP_ServiceAttributesRequest.
|
||||
*
|
||||
* @param attributeValues values returned by server within
|
||||
* SDP_ServiceAttributesResponse.
|
||||
*
|
||||
* @param transactionID ID of transaction response recieved within.
|
||||
*/
|
||||
abstract public void serviceAttributeResponse(int[] attrIDs,
|
||||
DataElement[] attributeValues, int transactionID);
|
||||
|
||||
/*
|
||||
* Informs this listener about attributes of fisrt found service record.
|
||||
*/
|
||||
abstract public void serviceSearchAttributeResponse(int[] attrIDs,
|
||||
DataElement[] attributeValues, int transactionID);
|
||||
|
||||
public static int[] extendByStandardAttrs( int[] attrSet )
|
||||
throws IllegalArgumentException {
|
||||
/* Extend attributes IDs with standard values from the spec except duplicates */
|
||||
Hashtable uniquies = new Hashtable();
|
||||
/* appending by user required attributes */
|
||||
if (attrSet != null) {
|
||||
if (attrSet.length <= 0 || attrSet.length > ServiceRecordImpl.RETRIEVABLE_MAX) {
|
||||
throw new IllegalArgumentException("Invalid attribute set length");
|
||||
}
|
||||
for (int i=0; i<attrSet.length; i++) {
|
||||
if (uniquies.put(new Integer(attrSet[i]), FAKE_VALUE) != null ) {
|
||||
throw new IllegalArgumentException( "Duplicate attribute Ox" + Integer.toHexString(attrSet[i]) );
|
||||
}
|
||||
if ((attrSet[i] & MASK_OVERFLOW) != 0) {
|
||||
throw new IllegalArgumentException("Illegal attribute ID");
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Adding standard attributes */
|
||||
for (int i=0; i<5; i++) {
|
||||
uniquies.put(new Integer(i), FAKE_VALUE);
|
||||
}
|
||||
int [] extendedAttributes = new int[uniquies.size()];
|
||||
Enumeration attrs = uniquies.keys();
|
||||
for( int i=0; attrs.hasMoreElements(); i++ ) {
|
||||
extendedAttributes[i] = ((Integer) attrs.nextElement()).intValue();
|
||||
}
|
||||
attrSort(extendedAttributes);
|
||||
return extendedAttributes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sorts an integer array in ascending order.
|
||||
*/
|
||||
private static void attrSort(int[] data) {
|
||||
|
||||
for (int k = 0; k < data.length - 1; k++)
|
||||
{
|
||||
boolean isSorted = true;
|
||||
|
||||
for (int i = 1; i < data.length - k; i++)
|
||||
{
|
||||
if (data[i] < data[i - 1])
|
||||
{
|
||||
int tmp = data[i];
|
||||
data[i] = data[i - 1];
|
||||
data[i - 1] = tmp;
|
||||
|
||||
isSorted = false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (isSorted)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static UUID[] removeDuplicatedUuids( UUID[] uuidSet )
|
||||
throws IllegalArgumentException, NullPointerException {
|
||||
Hashtable uniquies = new Hashtable();
|
||||
UUID[] uuids;
|
||||
if ((uuidSet != null) && (uuidSet.length > 0)) {
|
||||
/* uuid checking */
|
||||
for (int i = 0; i < uuidSet.length; i++) {
|
||||
|
||||
if (uuidSet[i] == null) {
|
||||
throw new NullPointerException("Invalid UUID. Null");
|
||||
}
|
||||
|
||||
/* check UUID duplication */
|
||||
if (uniquies.put(uuidSet[i], FAKE_VALUE) != null) {
|
||||
throw new IllegalArgumentException("Duplicated UUID: " +
|
||||
uuidSet[i]);
|
||||
}
|
||||
}
|
||||
|
||||
uuids = new UUID[uniquies.size()];
|
||||
Enumeration keys = uniquies.keys();
|
||||
|
||||
for (int i = 0; keys.hasMoreElements(); i++) {
|
||||
uuids[i] = (UUID) keys.nextElement();
|
||||
}
|
||||
} else {
|
||||
uuids = new UUID[0];
|
||||
}
|
||||
return uuids;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.bluetooth.RemoteDevice;
|
||||
import javax.bluetooth.DataElement;
|
||||
import javax.bluetooth.ServiceRecord;
|
||||
import javax.bluetooth.UUID;
|
||||
|
||||
import com.sun.jsr082.bluetooth.SDPClient;
|
||||
|
||||
/*
|
||||
* This class provides functionality of DiscoveryAgent.selectService()
|
||||
* (see JSR 82 texts for details) using SDP serviceAttribute request.
|
||||
*/
|
||||
class ServiceSelector extends ServiceSearcherBase {
|
||||
|
||||
/* Set to false in RR version - then the javac skip the code. */
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/* this class name for debug. */
|
||||
private static final String cn = "ServiceSelector";
|
||||
|
||||
/* Keeps attributes values retrieved from SDP_ServiceAttributeResponse. */
|
||||
private DataElement[] attrValues = null;
|
||||
|
||||
/* Keeps an IOException if any occured or SDP_ErrorResponceRecieved. */
|
||||
private IOException ioExcpt = null;
|
||||
|
||||
/*
|
||||
* Creates ServiceDiscoverer and save all required info in it.
|
||||
*/
|
||||
ServiceSelector(int[] attrSet, UUID[] uuidSet, RemoteDevice btDev) {
|
||||
super(attrSet, uuidSet, btDev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Recieves error information retrieved from SDP_ErrorResponse and
|
||||
* copmpletes the request activity by error reason.
|
||||
*/
|
||||
public void errorResponse(int errorCode, String info, int transactionID) {
|
||||
if (DEBUG) {
|
||||
System.out.println(cn + ".errorResponse: called");
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
ioExcpt = new IOException(info);
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Base class method not relevant to this subclass, it must never be called.
|
||||
*/
|
||||
public void serviceSearchResponse(int[] handleList, int transactionID) {
|
||||
if (DEBUG) {
|
||||
System.out.println(cn + ".serviceSearchResponse: unexpected call");
|
||||
}
|
||||
throw new RuntimeException("unexpected call");
|
||||
}
|
||||
|
||||
/*
|
||||
* Base class method not relevant to this subclass, it must never be called.
|
||||
*/
|
||||
public void serviceAttributeResponse(int[] attrIDs,
|
||||
DataElement[] attributeValues, int transactionID) {
|
||||
if (DEBUG) {
|
||||
System.out.println(cn +
|
||||
".serviceAttributeResponse: unexpected call");
|
||||
}
|
||||
throw new RuntimeException("unexpected call");
|
||||
}
|
||||
|
||||
/*
|
||||
* Receives arrays of service record attributes and their values retrieved
|
||||
* from SDP_ServiceSearchAttributeResponse.
|
||||
*/
|
||||
public void serviceSearchAttributeResponse(int[] attrIDs,
|
||||
DataElement[] attributeValues, int transactionID) {
|
||||
if (DEBUG) {
|
||||
System.out.println(cn + ".serviceSearchAttributeResponse: called");
|
||||
}
|
||||
synchronized (this) {
|
||||
attrSet = attrIDs;
|
||||
attrValues = attributeValues;
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs SERVICESEARCHATTRIBUTE transaction and returns newly created
|
||||
* <code>ServiceRecordImpl</code> instance with attributes and values
|
||||
* returned by server within SDP_serviceSearchAttributeResponse.
|
||||
*
|
||||
* @return newly created <code>ServiceRecordImpl</code> instance with
|
||||
* attributes and values returned by server if the transaction has completed
|
||||
* successfully and attributes list retrieved is not empty,
|
||||
* <code>null</code> otherwise.
|
||||
*/
|
||||
ServiceRecord getServiceRecord() {
|
||||
SDPClient sdp = null;
|
||||
short transactionID = SDPClientTransactionBase.newTransactionID();
|
||||
|
||||
try {
|
||||
// sdp = new JavaSDPClient(btDev.getBluetoothAddress());
|
||||
sdp = ServiceDiscovererFactory.getServiceDiscoverer().
|
||||
getSDPClient(btDev.getBluetoothAddress());
|
||||
|
||||
if (sdp != null) {
|
||||
sdp.serviceSearchAttributeRequest(attrSet, uuidSet,
|
||||
transactionID, this);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
if (DEBUG) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
ioExcpt = ioe;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (ioExcpt == null && attrValues == null && sdp != null) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException ie) {
|
||||
// ignore (break waiting)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (sdp != null) {
|
||||
sdp.close();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
if (DEBUG) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
// ignore
|
||||
}
|
||||
|
||||
if (ioExcpt != null) {
|
||||
return null;
|
||||
} else if (attrValues == null) {
|
||||
return null;
|
||||
} else if (attrValues.length == 0) {
|
||||
return null;
|
||||
} else {
|
||||
return new ServiceRecordImpl(btDev, attrSet, attrValues);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,435 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
|
||||
}
|
|
@ -0,0 +1,437 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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 com.sun.jsr082.bluetooth.BluetoothUrl;
|
||||
import javax.bluetooth.*;
|
||||
import java.util.Enumeration;
|
||||
import com.sun.j2me.app.AppPackage;
|
||||
// #ifndef NO_PUSH
|
||||
import com.sun.jsr082.bluetooth.BluetoothPush;
|
||||
// #endif
|
||||
import com.sun.jsr082.bluetooth.SDDB;
|
||||
import com.sun.jsr082.bluetooth.BCC;
|
||||
import com.sun.jsr082.bluetooth.ServiceRecordImpl;
|
||||
import com.sun.jsr082.bluetooth.BluetoothNotifier;
|
||||
import com.sun.jsr082.bluetooth.SDP;
|
||||
|
||||
/*
|
||||
* Implementation of <code>L2CAPConnectionNotifier</code>.
|
||||
* An instance is created when
|
||||
* <code>Connector.open("btltcap://localhost...") </code>
|
||||
* is called from server application.
|
||||
*/
|
||||
public class L2CAPNotifierImpl extends BluetoothNotifier
|
||||
implements L2CAPConnectionNotifier {
|
||||
|
||||
/* 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();
|
||||
|
||||
/*
|
||||
* Identidies this connection at native layer,
|
||||
* <code>-1<code> if connection is not open.
|
||||
*/
|
||||
private int handle = -1;
|
||||
|
||||
// IMPL_NOTE make it back private when moving emulation
|
||||
// below porting layer completed
|
||||
/*
|
||||
* Temporary stores peer's native handle for accepted connection.
|
||||
* Used by <code>doAccept</code> method.
|
||||
*/
|
||||
int peerHandle = -1;
|
||||
|
||||
/*
|
||||
* Temporary stores remote device address for accepted connection.
|
||||
*/
|
||||
byte[] peerAddress = new byte[6];
|
||||
|
||||
/* Indicates whether notifier is listening for incoming connections. */
|
||||
protected boolean isListenMode = false;
|
||||
|
||||
/*
|
||||
* Negotiated ReceiveMTU and TransmitMTU.
|
||||
* 16 high bits is ReceiveMTU, 16 low bits is TransmitMTU.
|
||||
*
|
||||
* This packeted value is returned by accept0 method and copied to
|
||||
* L2CAPConnectionImpl by it's constructor.
|
||||
*/
|
||||
int mtus = (((-1) << 16) & 0xFFFF0000) & ((-1) & 0xFFFF);
|
||||
|
||||
/* Keeps PSM value for service record validation. */
|
||||
private DataElement PSM;
|
||||
|
||||
/* Keeps L2CAP UUID for service record validation. */
|
||||
static final DataElement L2CAP_UUID =
|
||||
new DataElement(DataElement.UUID, new UUID(0x0100));
|
||||
|
||||
/* Bluetooth PushRegistry handle, used in native methods only. */
|
||||
private int pushHandle = 0;
|
||||
|
||||
/*
|
||||
* Creates instance of <code>L2CAPNotifierImpl</code>.
|
||||
*
|
||||
* @param url <code>BluetoothUrl</code> that represents server
|
||||
* connection string to create notifier for
|
||||
* @param mode I/O access mode
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
protected L2CAPNotifierImpl(BluetoothUrl url, int mode) throws IOException {
|
||||
super(url, mode);
|
||||
|
||||
AppPackage app = AppPackage.getInstance();
|
||||
|
||||
int appId = (app != null) ? app.getId() : 0;
|
||||
String connUrl = "btl2cap:" + url.caseSensitiveUrl;
|
||||
|
||||
// check whether suiteId is valid.
|
||||
// for example, it can be null if L2CAPNotifier is created as SDP server
|
||||
// in PushRegistry inbound connection watcher thread that can be started
|
||||
// before MIDlet start.
|
||||
// #ifndef NO_PUSH
|
||||
if ((app != null) && pushCheckout(connUrl, appId)) {
|
||||
serviceRec = BluetoothPush.getServiceRecord(this, connUrl);
|
||||
checkServiceRecord();
|
||||
} else {
|
||||
serviceRec = createServiceRecord(this, url, doCreate(url));
|
||||
}
|
||||
// #else
|
||||
serviceRec = createServiceRecord(this, url, doCreate(url));
|
||||
// #endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks out (takes ownership of) an active server connection maintained
|
||||
* by push subsystem.
|
||||
*
|
||||
* @param url URL used during registration of the push entry
|
||||
* @param suiteId suite id
|
||||
* @return true if the operation succeeds, false otherwise
|
||||
*/
|
||||
// #ifndef NO_PUSH
|
||||
private native boolean pushCheckout(String url, int suiteId);
|
||||
// #endif
|
||||
|
||||
|
||||
/*
|
||||
* Creates an empty service record for the given URL and PSM value.
|
||||
*
|
||||
* @param notifier L2CAP notifier object to be associated with the record
|
||||
* @param url URL from which a new record is constructed
|
||||
* @param psm PSM value assigned to the notifier
|
||||
* @return a new service record instance
|
||||
*/
|
||||
public static ServiceRecordImpl createServiceRecord(
|
||||
L2CAPNotifierImpl notifier, BluetoothUrl url, int psm) {
|
||||
DataElement serviceList = new DataElement(DataElement.DATSEQ);
|
||||
serviceList.addElement(new DataElement(DataElement.UUID,
|
||||
new UUID(url.uuid, false)));
|
||||
DataElement protocolList = new DataElement(DataElement.DATSEQ);
|
||||
DataElement protocol = new DataElement(DataElement.DATSEQ);
|
||||
protocol.addElement(L2CAP_UUID);
|
||||
if (notifier != null) {
|
||||
notifier.PSM = new DataElement(DataElement.U_INT_2, psm);
|
||||
protocol.addElement(notifier.PSM);
|
||||
} else {
|
||||
protocol.addElement(new DataElement(DataElement.U_INT_2, psm));
|
||||
}
|
||||
protocolList.addElement(protocol);
|
||||
int[] attrIDs;
|
||||
DataElement[] attrVals;
|
||||
if (url.name != null) {
|
||||
DataElement name = new DataElement(DataElement.STRING, url.name);
|
||||
attrIDs = new int[] {
|
||||
ServiceRecordImpl.SERVICE_CLASS_ATTR_ID,
|
||||
ServiceRecordImpl.PROTOCOL_DESCRIPTOR_LIST,
|
||||
ServiceRecordImpl.NAME_ATTR_ID
|
||||
};
|
||||
attrVals = new DataElement[] { serviceList, protocolList, name };
|
||||
} else {
|
||||
attrIDs = new int[] {
|
||||
ServiceRecordImpl.SERVICE_CLASS_ATTR_ID,
|
||||
ServiceRecordImpl.PROTOCOL_DESCRIPTOR_LIST
|
||||
};
|
||||
attrVals = new DataElement[] { serviceList, protocolList };
|
||||
}
|
||||
return new ServiceRecordImpl(notifier, attrIDs, attrVals);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates an empty service record for the given URL.
|
||||
*
|
||||
* @param url URL from which a new record is constructed
|
||||
* @return a new service record instance
|
||||
*/
|
||||
public static ServiceRecordImpl createServiceRecord(String url) {
|
||||
return createServiceRecord(null, new BluetoothUrl(url), 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensures that the service record is valid.
|
||||
*
|
||||
* @throws ServiceRegistrationException in case described in the
|
||||
* JSR specification
|
||||
*/
|
||||
protected void checkServiceRecord() throws ServiceRegistrationException {
|
||||
synchronized (serviceRec) {
|
||||
// check if the ServiceClassIDList is not missing
|
||||
if (serviceRec.getAttributeValue(
|
||||
ServiceRecordImpl.SERVICE_CLASS_ATTR_ID) == null) {
|
||||
throw new ServiceRegistrationException(
|
||||
"ServiceClassIDList is missing.");
|
||||
}
|
||||
// check the ProtocolList is not missed
|
||||
DataElement protocolList = serviceRec.getAttributeValue(
|
||||
ServiceRecordImpl.PROTOCOL_DESCRIPTOR_LIST);
|
||||
if (protocolList == null) {
|
||||
throw new ServiceRegistrationException(
|
||||
"ProtocolDescriptorList is missing.");
|
||||
}
|
||||
Enumeration protocolListEnum =
|
||||
(Enumeration)protocolList.getValue();
|
||||
while (protocolListEnum.hasMoreElements()) {
|
||||
Enumeration protocolEnum = (Enumeration)((DataElement)
|
||||
protocolListEnum.nextElement()).getValue();
|
||||
// check that the L2CAP UUID is not missing, check the PSM
|
||||
// is not missing and the value has not changed
|
||||
if (protocolEnum.hasMoreElements()) {
|
||||
if (compareDataElements((DataElement)protocolEnum.
|
||||
nextElement(), L2CAP_UUID)) {
|
||||
if (!protocolEnum.hasMoreElements()) {
|
||||
throw new ServiceRegistrationException(
|
||||
"PSM value is missing.");
|
||||
}
|
||||
if (PSM == null) {
|
||||
PSM = new DataElement(DataElement.U_INT_2,
|
||||
((DataElement)protocolEnum.
|
||||
nextElement()).getLong());
|
||||
return;
|
||||
}
|
||||
if (!compareDataElements((DataElement)protocolEnum.
|
||||
nextElement(), PSM)) {
|
||||
throw new ServiceRegistrationException(
|
||||
"PSM value has changed.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new ServiceRegistrationException("L2CAP UUID is missing.");
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensures that this notifier can accept connections.
|
||||
*
|
||||
* @throws IOException if notifier is closed, device is not
|
||||
* in connectable mode, or service record is invalid
|
||||
*/
|
||||
private void ensureConnectable() throws IOException {
|
||||
if (isClosed) {
|
||||
throw new BluetoothConnectionException(
|
||||
BluetoothConnectionException.FAILED_NOINFO,
|
||||
"Notifier is closed.");
|
||||
}
|
||||
if (!BCC.getInstance().isConnectable()) {
|
||||
throw new BluetoothStateException("The device is not connectable.");
|
||||
}
|
||||
checkServiceRecord();
|
||||
}
|
||||
|
||||
/*
|
||||
* Accepts connections blocking until incoming client connection appears.
|
||||
*
|
||||
* @return connection to a client just accepted.
|
||||
*
|
||||
* @throws IOException if notifier is closed or device is not
|
||||
* in connectable mode.
|
||||
*/
|
||||
public L2CAPConnection acceptAndOpen() throws IOException {
|
||||
ensureConnectable();
|
||||
|
||||
// switch on listen mode if it has not been done yet
|
||||
doListen();
|
||||
|
||||
// adds the service record if it is not yet in SDDB
|
||||
SDDB.getInstance().updateServiceRecord(serviceRec);
|
||||
|
||||
L2CAPConnection client;
|
||||
do {
|
||||
ensureConnectable();
|
||||
|
||||
// accept incoming connections if any
|
||||
client = doAccept();
|
||||
} while (client == null);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
/*
|
||||
* Closes this notifier making corresponding service record inaccessible.
|
||||
* updates the information on the communication server.
|
||||
*
|
||||
* @exception IOException if an error occured lower in Bluetooth stack.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
isClosed = true;
|
||||
|
||||
SDDB.getInstance().removeServiceRecord(serviceRec);
|
||||
|
||||
doClose();
|
||||
}
|
||||
|
||||
/*
|
||||
* Force listen mode by calling underlying stack methods.
|
||||
*
|
||||
* @throws IOException if an error occured
|
||||
*/
|
||||
private void doListen() throws IOException {
|
||||
// force listening if it had not been done yet
|
||||
if (!isListenMode) {
|
||||
listen0();
|
||||
|
||||
isListenMode = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Force Bluetooth stack to listen for incoming client connections.
|
||||
*
|
||||
* Note: the method gets native connection handle directly from
|
||||
* <code>handle<code> field of <code>L2CAPNotifierImpl</code> object.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
private native void listen0() throws IOException;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Advertises acception by calling underlying stack methods.
|
||||
*
|
||||
* @return L2CAPConnectionImpl instance to work with accepted client
|
||||
*/
|
||||
protected L2CAPConnection doAccept() throws IOException {
|
||||
if (!isListenMode) {
|
||||
throw new BluetoothStateException("Device is not in listen mode");
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: native handle is set to peerHandleID field directly by accept0
|
||||
* method and retrieved by L2CAPConnectionImpl constructor.
|
||||
*/
|
||||
mtus = accept0();
|
||||
|
||||
return new L2CAPConnectionImpl(url, mode, this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Accepts incoming client connection request.
|
||||
*
|
||||
* Note: the method gets native connection handle directly from
|
||||
* <code>handle<code> field of <code>L2CAPNotifierImpl</code> object.
|
||||
*
|
||||
* Note: new native connection handle to work with accepted incoming
|
||||
* client connection is setted directly to <code>handle</code> field of
|
||||
* appropriate <code>L2CAPConnectionImpl</code> object.
|
||||
*
|
||||
* @return Negotiated ReceiveMTU and TransmitMTU.
|
||||
* 16 high bits is ReceiveMTU, 16 low bits is TransmitMTU.
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
protected native int accept0() throws IOException;
|
||||
|
||||
|
||||
/*
|
||||
* Closes this notifier at native layer.
|
||||
*/
|
||||
protected void doClose() throws IOException {
|
||||
close0();
|
||||
}
|
||||
/*
|
||||
* Closes this server connection.
|
||||
* Releases all native resources (such as sockets) owned by this notifier.
|
||||
*
|
||||
* Note: the method gets native connection handle directly from
|
||||
* <code>handle<code> field of <code>L2CAPNotifierImpl</code> object.
|
||||
*
|
||||
* @throws IOException IOException if an I/O error occurs
|
||||
*/
|
||||
private native void close0() throws IOException;
|
||||
|
||||
|
||||
/*
|
||||
* Creates an instanse of server connection at native layer.
|
||||
*
|
||||
* @param url <code>BluetoothUrl</code> that represents server
|
||||
* connection string to create notifier for
|
||||
* @return selected psm value to listen for incoming connections on
|
||||
*/
|
||||
private int doCreate(BluetoothUrl url) throws IOException {
|
||||
return create0(url.receiveMTU, url.transmitMTU, url.authenticate,
|
||||
url.authorize, url.encrypt, url.master);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a server connection.
|
||||
*
|
||||
* Note: the method sets native connection handle directly to
|
||||
* <code>handle<code> field of <code>L2CAPNotifierImpl</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 authz <code>true</code> if authorization 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
|
||||
* @return selected psm value to listen for incoming connections on
|
||||
* @throws IOException IOException if an I/O error occurs
|
||||
*/
|
||||
private native int create0(int imtu, int omtu, boolean auth, boolean authz,
|
||||
boolean enc, boolean master) throws IOException;
|
||||
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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 javax.microedition.io.Connection;
|
||||
import javax.bluetooth.L2CAPConnection;
|
||||
import javax.bluetooth.BluetoothConnectionException;
|
||||
|
||||
import com.sun.jsr082.bluetooth.BluetoothUrl;
|
||||
import com.sun.jsr082.bluetooth.BluetoothProtocol;
|
||||
import com.sun.j2me.security.BluetoothPermission;
|
||||
import com.sun.j2me.main.Configuration;
|
||||
|
||||
/*
|
||||
* Provides "btl2cap" protocol implementation
|
||||
*/
|
||||
public class Protocol extends BluetoothProtocol {
|
||||
/* Keeps maximum MTU supported by the BT stack. */
|
||||
static final int MAX_STACK_MTU;
|
||||
|
||||
static {
|
||||
int maxReceiveMTU;
|
||||
try {
|
||||
maxReceiveMTU = Integer.parseInt(System.getProperty(
|
||||
"bluetooth.l2cap.receiveMTU.max"));
|
||||
} catch (NumberFormatException e) {
|
||||
maxReceiveMTU = L2CAPConnection.DEFAULT_MTU;
|
||||
}
|
||||
MAX_STACK_MTU = maxReceiveMTU;
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructs an instance.
|
||||
*/
|
||||
public Protocol() {
|
||||
super(BluetoothUrl.L2CAP);
|
||||
}
|
||||
|
||||
/*
|
||||
* Cheks permissions and opens requested connection.
|
||||
*
|
||||
* @param token security token passed by calling class
|
||||
* @param url <code>BluetoothUrl</code> instance that defines required
|
||||
* connection stringname the URL without protocol name and colon
|
||||
* @param mode Connector.READ_WRITE or Connector.READ or Connector.WRITE
|
||||
*
|
||||
* @return a notifier in case of server connection string, open connection
|
||||
* in case of client one.
|
||||
*
|
||||
* @exception IOException if opening connection fails.
|
||||
*/
|
||||
public Connection openPrim(BluetoothUrl url, int mode)
|
||||
throws IOException {
|
||||
return openPrimImpl(url, mode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensures URL parameters have valid values. Sets receiveMTU if undefined.
|
||||
* @param url URL to check
|
||||
* @exception IllegalArgumentException if invalid url parameters found
|
||||
* @exception BluetoothConnectionException if url parameters are not
|
||||
* acceptable due to Bluetooth stack limitations
|
||||
*/
|
||||
protected void checkUrl(BluetoothUrl url)
|
||||
throws IllegalArgumentException, BluetoothConnectionException {
|
||||
|
||||
if (url.receiveMTU == -1) {
|
||||
url.receiveMTU = L2CAPConnection.DEFAULT_MTU;
|
||||
}
|
||||
|
||||
if (url.isSystem()) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.checkUrl(url);
|
||||
|
||||
if (!url.isServer && (url.port <= 0x1000 || url.port >= 0xffff ||
|
||||
((url.port & 1) != 1) || ((url.port & 0x100) != 0))) {
|
||||
throw new IllegalArgumentException("Invalid PSM: " + url.port);
|
||||
}
|
||||
|
||||
// IMPL_NOTE BluetoothConnectionException should be thrown here
|
||||
// It is temporary substituted by IllegalArgumentException
|
||||
// to pass TCK succesfully. To be changed back when fixed
|
||||
// TCK arrives. The erroneous TCK test is
|
||||
// javasoft.sqe.tests.api.javax.bluetooth.Connector.L2Cap.
|
||||
// openClientTests.L2Cap1014()
|
||||
//
|
||||
// Correct code here is
|
||||
// throw new BluetoothConnectionException(
|
||||
// BluetoothConnectionException.UNACCEPTABLE_PARAMS,
|
||||
// <message>);
|
||||
if (url.receiveMTU < L2CAPConnection.MINIMUM_MTU) {
|
||||
throw new IllegalArgumentException(
|
||||
"Receive MTU is too small");
|
||||
}
|
||||
|
||||
if (url.receiveMTU > MAX_STACK_MTU) {
|
||||
throw new IllegalArgumentException("Receive MTU is too large");
|
||||
}
|
||||
|
||||
if (url.transmitMTU != -1 && url.transmitMTU > MAX_STACK_MTU) {
|
||||
throw new BluetoothConnectionException(
|
||||
BluetoothConnectionException.UNACCEPTABLE_PARAMS,
|
||||
"Transmit MTU is too large");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensures that permissions are proper and creates client side connection.
|
||||
* @param token security token if passed by caller, or <code>null</code>
|
||||
* @param mode I/O access mode
|
||||
* @return proper <code>L2CAPConnectionImpl</code> instance
|
||||
* @exception IOException if openning connection fails.
|
||||
*/
|
||||
protected Connection clientConnection(int mode)
|
||||
throws IOException {
|
||||
checkForPermission(BluetoothPermission.BLUETOOTH_CLIENT);
|
||||
return new L2CAPConnectionImpl(url, mode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensures that permissions are proper and creates required notifier at
|
||||
* server side.
|
||||
* @param token security token if passed by caller, or <code>null</code>
|
||||
* @param mode I/O access mode
|
||||
* @return proper <code>L2CAPNotifierImpl</code> instance
|
||||
* @exception IOException if openning connection fails
|
||||
*/
|
||||
protected Connection serverConnection(int mode)
|
||||
throws IOException {
|
||||
checkForPermission(BluetoothPermission.BLUETOOTH_SERVER);
|
||||
return new L2CAPNotifierImpl(url, mode);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,530 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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.btspp;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import javax.microedition.io.StreamConnection;
|
||||
import javax.bluetooth.BluetoothConnectionException;
|
||||
import com.sun.jsr082.bluetooth.BluetoothUrl;
|
||||
import com.sun.jsr082.bluetooth.BluetoothUtils;
|
||||
import com.sun.jsr082.bluetooth.BluetoothConnection;
|
||||
|
||||
/*
|
||||
* Bluetooth Serial Port Profile connection implementation.
|
||||
*/
|
||||
public class BTSPPConnectionImpl extends BluetoothConnection
|
||||
implements StreamConnection {
|
||||
|
||||
/* 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();
|
||||
|
||||
/*
|
||||
* Identidies this connection at native layer, <code>-1<code>
|
||||
* if connection is not open.
|
||||
*/
|
||||
private int handle = -1;
|
||||
|
||||
/* Flag to identify if an input stream is opened for this connection. */
|
||||
private boolean isOpened = false;
|
||||
|
||||
/* Flag to identify if an output stream is opened for this connection. */
|
||||
private boolean osOpened = false;
|
||||
|
||||
/* Open streams counter. */
|
||||
private int objects = 1;
|
||||
|
||||
/*
|
||||
* Constructs an instance and opens connection.
|
||||
*
|
||||
* @param url keeps connection details
|
||||
* @param mode I/O access mode
|
||||
* @exception IOException if connection fails
|
||||
*/
|
||||
protected BTSPPConnectionImpl(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>BTSPPNotifierImpl</code> instance
|
||||
* temporary storing native peer handle
|
||||
* @exception IOException if connection fails
|
||||
*/
|
||||
protected BTSPPConnectionImpl(BluetoothUrl url,
|
||||
int mode, BTSPPNotifierImpl 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);
|
||||
}
|
||||
|
||||
setRemoteDevice();
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves native connection handle from temporary storage
|
||||
* inside <code>BTSPPNotifierImpl</code> instance
|
||||
* and sets it to this <code>BTSPPConnectionImpl</code> instance.
|
||||
*
|
||||
* Note: the method sets native connection handle directly to
|
||||
* <code>handle<code> field of <code>BTSPPConnectionImpl</code> object.
|
||||
*
|
||||
* @param notif reference to corresponding <code>BTSPPNotifierImpl</code>
|
||||
* instance storing native peer handle
|
||||
*/
|
||||
private native void setThisConnHandle0(BTSPPNotifierImpl notif);
|
||||
|
||||
/*
|
||||
* Retrieves remote address for the connection.
|
||||
*
|
||||
* @return remote address
|
||||
*/
|
||||
public String getRemoteDeviceAddress() {
|
||||
return BluetoothUtils.getAddressString(remoteDeviceAddress);
|
||||
}
|
||||
|
||||
/*
|
||||
* Open and return a data input stream for a connection.
|
||||
*
|
||||
* @return An input stream
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public final DataInputStream openDataInputStream() throws IOException {
|
||||
return new DataInputStream(openInputStream());
|
||||
}
|
||||
|
||||
/*
|
||||
* Open and return a data output stream for a connection.
|
||||
*
|
||||
* @return An output stream
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public final DataOutputStream openDataOutputStream() throws IOException {
|
||||
return new DataOutputStream(openOutputStream());
|
||||
}
|
||||
|
||||
/*
|
||||
* Open and return an input stream for a connection.
|
||||
*
|
||||
* @return An input stream
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public final InputStream openInputStream() throws IOException {
|
||||
checkOpen();
|
||||
checkReadMode();
|
||||
synchronized (this) {
|
||||
if (isOpened) {
|
||||
throw new IOException("No more input streams");
|
||||
}
|
||||
isOpened = true;
|
||||
objects++;
|
||||
}
|
||||
return new SPPInputStream();
|
||||
}
|
||||
|
||||
/*
|
||||
* Open and return an output stream for a connection.
|
||||
*
|
||||
* @return An output stream
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public final OutputStream openOutputStream() throws IOException {
|
||||
checkOpen();
|
||||
checkWriteMode();
|
||||
synchronized (this) {
|
||||
if (osOpened) {
|
||||
throw new IOException("No more output streams");
|
||||
}
|
||||
osOpened = true;
|
||||
objects++;
|
||||
}
|
||||
return new SPPOutputStream();
|
||||
}
|
||||
|
||||
/*
|
||||
* Input stream implementation for BTSPPConnection
|
||||
*/
|
||||
private final class SPPInputStream extends InputStream {
|
||||
/* Indicates whether the stream is closed. */
|
||||
private boolean isClosed;
|
||||
|
||||
/*
|
||||
* Reads the next byte of data from the input stream.
|
||||
*
|
||||
* @return the next byte of data, or <code>-1</code> if the end of the
|
||||
* stream is reached.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public int read() throws IOException {
|
||||
byte[] buf = new byte[1];
|
||||
int res = read(buf);
|
||||
if (res != -1)
|
||||
res = buf[0] & 0xFF;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads some number of bytes from the input stream and stores them into
|
||||
* the buffer array <code>buf</code>.
|
||||
*
|
||||
* @param buf the buffer into which the data is read.
|
||||
* @return the total number of bytes read into the buffer, or
|
||||
* <code>-1</code> is there is no more data because the end
|
||||
* of the stream has been reached.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
* @see java.io.InputStream#read(byte[], int, int)
|
||||
*/
|
||||
public int read(byte[] buf) throws IOException {
|
||||
return read(buf, 0, buf.length);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads up to <code>len</code> bytes of data from the input stream into
|
||||
* an array of bytes. An attempt is made to read as many as
|
||||
* <code>len</code> bytes, but a smaller number may be read, possibly
|
||||
* zero. The number of bytes actually read is returned as an integer.
|
||||
* @param buf the buffer into which the data is read.
|
||||
* @param off the start offset in array <code>buf</code>
|
||||
* at which the data is written.
|
||||
* @param len the maximum number of bytes to read.
|
||||
* @return the total number of bytes read into the buffer, or
|
||||
* <code>-1</code> if there is no more data because the end
|
||||
* of the stream has been reached.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
* @see java.io.InputStream#read()
|
||||
*/
|
||||
public int read(byte[] buf, int off, int len) throws IOException {
|
||||
if (isClosed) {
|
||||
throw new IOException("Stream is closed");
|
||||
}
|
||||
|
||||
if ((off < 0) || (len < 0) || (off + len > buf.length)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int res;
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
res = receive0(buf, off, len);
|
||||
}
|
||||
|
||||
// convert the 'end of data' to the 'end of stream'
|
||||
if (res == 0) {
|
||||
res = -1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the number of bytes that can be read (or skipped over) from
|
||||
* this input stream without blocking by the next caller of a method for
|
||||
* this input stream. The next caller might be the same thread or
|
||||
* another thread.
|
||||
*
|
||||
* @return the number of bytes that can be read from this input
|
||||
* stream without blocking.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public int available() throws IOException {
|
||||
if (isClosed) {
|
||||
throw new IOException("Stream is closed");
|
||||
}
|
||||
return available0();
|
||||
}
|
||||
|
||||
/*
|
||||
* Closes this input stream and releases any system resources associated
|
||||
* with the stream.
|
||||
*
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
synchronized (BTSPPConnectionImpl.this) {
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
isClosed = true;
|
||||
objects--;
|
||||
if (objects == 0) {
|
||||
close0();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Output stream implementation for BTSPPConnection
|
||||
*/
|
||||
private final class SPPOutputStream extends OutputStream {
|
||||
/* Indicates whether the stream is closed. */
|
||||
private boolean isClosed;
|
||||
|
||||
/*
|
||||
* Writes the specified byte to this output stream.
|
||||
* @param b the <code>byte</code>.
|
||||
* @exception IOException if an I/O error occurs. In particular,
|
||||
* an <code>IOException</code> may be thrown if the
|
||||
* output stream has been closed.
|
||||
*/
|
||||
public void write(int b) throws IOException {
|
||||
write(new byte[] { (byte)b });
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes <code>size</code> bytes from the specified byte array
|
||||
* starting at offset <code>offset</code> to this output stream.
|
||||
* @param buf the data.
|
||||
* @param offset the start offset in the data.
|
||||
* @param size the number of bytes to write.
|
||||
* @exception IOException if an I/O error occurs. In particular,
|
||||
* an <code>IOException</code> is thrown if the output
|
||||
* stream is closed.
|
||||
*/
|
||||
public void write(byte[] buf, int offset, int size)
|
||||
throws IOException {
|
||||
if (isClosed) {
|
||||
throw new IOException("Stream is closed");
|
||||
}
|
||||
|
||||
if (size < 0 || offset < 0 || offset + size > buf.length) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
while (size > 0) {
|
||||
int res = send0(buf, offset, size);
|
||||
|
||||
if (res <= 0) {
|
||||
throw new IOException("Data send failed");
|
||||
}
|
||||
|
||||
offset += res;
|
||||
size -= res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Closes this output stream and releases any system resources
|
||||
* associated with this stream.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
synchronized (BTSPPConnectionImpl.this) {
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
isClosed = true;
|
||||
objects--;
|
||||
if (objects == 0) {
|
||||
close0();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Closes this connection.
|
||||
* @throws IOException if I/O error.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
synchronized (this) {
|
||||
if (isClosed()) {
|
||||
return;
|
||||
}
|
||||
resetRemoteDevice();
|
||||
objects--;
|
||||
if (objects == 0) {
|
||||
close0();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* Reads data from a packet received via Bluetooth stack.
|
||||
*
|
||||
* Note: the method gets native connection handle directly from
|
||||
* <code>handle<code> field of <code>BTSPPConnectionImpl</code> object.
|
||||
*
|
||||
* @param buf the buffer to read to
|
||||
* @param off the start offset in array <code>buf</code>
|
||||
* 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,
|
||||
* <code>0</code> indicates end-of-data,
|
||||
* <code>-1</code> if there is no data available at this moment
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
protected native int receive0(byte[] buf, int off, int size)
|
||||
throws IOException;
|
||||
|
||||
/*
|
||||
* Returns the number of bytes available to be read from the connection
|
||||
* without blocking.
|
||||
*
|
||||
* Note: the method gets native connection handle directly from
|
||||
* <code>handle<code> field of <code>BTSPPConnectionImpl</code> object.
|
||||
*
|
||||
* @return the number of available bytes
|
||||
* @throws IOException if any I/O error occurs
|
||||
*/
|
||||
private native int available0() throws IOException;
|
||||
|
||||
/*
|
||||
* Sends the specified data via Bluetooth stack.
|
||||
*
|
||||
* 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 size the size of 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 size) throws IOException;
|
||||
|
||||
|
||||
/* Opens client connection. */
|
||||
private void doOpen() throws IOException {
|
||||
/*
|
||||
* create native connection object
|
||||
* Note: the method <code>create0</code> sets resulting native
|
||||
* connection handle directly to the field <code>handle<code>.
|
||||
*/
|
||||
create0(url.authenticate, url.encrypt, url.master);
|
||||
|
||||
byte[] address = BluetoothUtils.getAddressBytes(url.address);
|
||||
|
||||
try {
|
||||
// establish connection
|
||||
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>BTSPPConnectionImpl</code> object.
|
||||
*
|
||||
* @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(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>BTSPPConnectionImpl</code> object.
|
||||
*
|
||||
* @param addr bluetooth address of device to connect to
|
||||
* @param cn Channel number (CN) value
|
||||
* @throws IOException if any I/O error occurs
|
||||
*/
|
||||
private native void connect0(byte[] addr, int cn) throws IOException;
|
||||
|
||||
}
|
|
@ -0,0 +1,457 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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.btspp;
|
||||
|
||||
import javax.bluetooth.*;
|
||||
|
||||
import javax.microedition.io.StreamConnection;
|
||||
import javax.microedition.io.StreamConnectionNotifier;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
|
||||
import java.util.Vector;
|
||||
import java.util.Enumeration;
|
||||
/// IMPL_NOTE: revisit
|
||||
// #ifndef NO_PUSH
|
||||
import com.sun.jsr082.bluetooth.BluetoothPush;
|
||||
// #endif
|
||||
import com.sun.j2me.app.AppPackage;
|
||||
import com.sun.jsr082.bluetooth.SDDB;
|
||||
import com.sun.jsr082.bluetooth.BCC;
|
||||
import com.sun.jsr082.bluetooth.ServiceRecordImpl;
|
||||
import com.sun.jsr082.bluetooth.BluetoothNotifier;
|
||||
import com.sun.jsr082.bluetooth.BluetoothUrl;
|
||||
import com.sun.jsr082.obex.btgoep.BTGOEPNotifier;
|
||||
|
||||
/*
|
||||
* Bluetooth Serial Port Profile notifier implementation.
|
||||
*/
|
||||
public class BTSPPNotifierImpl extends BluetoothNotifier
|
||||
implements StreamConnectionNotifier {
|
||||
|
||||
/* 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();
|
||||
|
||||
/*
|
||||
* Identidies this connection at native layer,
|
||||
* <code>-1<code> if connection is not open.
|
||||
*/
|
||||
private int handle = -1;
|
||||
|
||||
// IMPL_NOTE make private when moving emul below the porting layer completed.
|
||||
/*
|
||||
* Temporary stores peer's native handle for accepted connection.
|
||||
* Used by <code>doAccept</code> method.
|
||||
*/
|
||||
int peerHandle = -1;
|
||||
|
||||
/*
|
||||
* Temporary stores remote device address for accepted connection.
|
||||
*/
|
||||
byte[] peerAddress = new byte[6];
|
||||
|
||||
/* Indicates whether notifier is listening for incoming connections. */
|
||||
protected boolean isListenMode = false;
|
||||
|
||||
/*
|
||||
* Channel Id.
|
||||
*/
|
||||
private int cid;
|
||||
|
||||
/* Keeps channel id for service record validation. */
|
||||
private DataElement CHANNEL_ID;
|
||||
|
||||
/* Keeps L2CAP UUID for service record validation. */
|
||||
static final DataElement L2CAP_UUID =
|
||||
new DataElement(DataElement.UUID, new UUID(0x0100));
|
||||
|
||||
/* Keeps RFCOMM UUID for service record validation. */
|
||||
static final DataElement RFCOMM_UUID =
|
||||
new DataElement(DataElement.UUID, new UUID(0x0003));
|
||||
|
||||
/* Keeps SPP UUID for service record validation. */
|
||||
static final DataElement SPP_UUID =
|
||||
new DataElement(DataElement.UUID, new UUID(0x1101));
|
||||
|
||||
/* Bluetooth PushRegistry handle, used in native methods only. */
|
||||
private int pushHandle = 0;
|
||||
|
||||
/*
|
||||
* Creates instance of <code>BTSPPNotifierImpl</code>.
|
||||
*
|
||||
* @param url <code>BluetoothUrl</code> that represents server
|
||||
* connection string to create notifier for.
|
||||
* @param mode I/O access mode
|
||||
* @throws IOException if there is no available channels to open connection
|
||||
* @throws ServiceRegistrationException if there is no available channel
|
||||
*/
|
||||
public BTSPPNotifierImpl(BluetoothUrl url, int mode) throws IOException,
|
||||
ServiceRegistrationException {
|
||||
super(url, mode);
|
||||
|
||||
AppPackage app = AppPackage.getInstance();
|
||||
String connUrl = "btspp:" + url.caseSensitiveUrl;
|
||||
// #ifndef NO_PUSH
|
||||
if (url.protocol == url.OBEX) {
|
||||
connUrl = "btgoep:" + url.caseSensitiveUrl;
|
||||
}
|
||||
if (app != null && pushCheckout(connUrl, app.getId())) {
|
||||
serviceRec = BluetoothPush.getServiceRecord(this, connUrl);
|
||||
checkServiceRecord();
|
||||
} else {
|
||||
serviceRec = createServiceRecord(this, url, doCreate(url));
|
||||
}
|
||||
// #else
|
||||
serviceRec = createServiceRecord(this, url, doCreate(url));
|
||||
// #endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks out (takes ownership of) an active server connection maintained
|
||||
* by push subsystem.
|
||||
*
|
||||
* @param url URL used during registration of the push entry
|
||||
* @param suiteId suite id
|
||||
* @return true if the operation succeeds, false otherwise
|
||||
*/
|
||||
// #ifndef NO_PUSH
|
||||
private native boolean pushCheckout(String url, int suiteId);
|
||||
// #endif
|
||||
|
||||
/*
|
||||
* Creates an empty service record for the given URL and channel value.
|
||||
*
|
||||
* @param notifier SPP notifier object to be associated with the record
|
||||
* @param url URL from which a new record is constructed
|
||||
* @param cn channel value assigned to the notifier
|
||||
* @return a new service record instance
|
||||
*/
|
||||
public static ServiceRecordImpl createServiceRecord(
|
||||
BTSPPNotifierImpl notifier, BluetoothUrl url, int cn) {
|
||||
DataElement serviceList = new DataElement(DataElement.DATSEQ);
|
||||
serviceList.addElement(new DataElement(DataElement.UUID,
|
||||
new UUID(url.uuid, false)));
|
||||
serviceList.addElement(SPP_UUID);
|
||||
DataElement protocolList = new DataElement(DataElement.DATSEQ);
|
||||
DataElement protocol = new DataElement(DataElement.DATSEQ);
|
||||
protocol.addElement(L2CAP_UUID);
|
||||
protocolList.addElement(protocol);
|
||||
protocol = new DataElement(DataElement.DATSEQ);
|
||||
protocol.addElement(RFCOMM_UUID);
|
||||
if (notifier != null) {
|
||||
notifier.CHANNEL_ID = new DataElement(DataElement.U_INT_1, cn);
|
||||
protocol.addElement(notifier.CHANNEL_ID);
|
||||
} else {
|
||||
protocol.addElement(new DataElement(DataElement.U_INT_1, cn));
|
||||
}
|
||||
protocolList.addElement(protocol);
|
||||
if (url.protocol == url.OBEX) {
|
||||
DataElement p = new DataElement(DataElement.DATSEQ);
|
||||
p.addElement(BTGOEPNotifier.DE_OBEX_UUID); // obex UUID
|
||||
protocolList.addElement(p);
|
||||
}
|
||||
int[] attrIDs;
|
||||
DataElement[] attrVals;
|
||||
if (url.name != null) {
|
||||
DataElement name = new DataElement(DataElement.STRING, url.name);
|
||||
attrIDs = new int[] {
|
||||
ServiceRecordImpl.SERVICE_CLASS_ATTR_ID,
|
||||
ServiceRecordImpl.PROTOCOL_DESCRIPTOR_LIST,
|
||||
ServiceRecordImpl.NAME_ATTR_ID
|
||||
};
|
||||
attrVals = new DataElement[] { serviceList, protocolList, name };
|
||||
} else {
|
||||
attrIDs = new int[] {
|
||||
ServiceRecordImpl.SERVICE_CLASS_ATTR_ID,
|
||||
ServiceRecordImpl.PROTOCOL_DESCRIPTOR_LIST
|
||||
};
|
||||
attrVals = new DataElement[] { serviceList, protocolList };
|
||||
}
|
||||
return new ServiceRecordImpl(notifier, attrIDs, attrVals);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates an empty service record for the given URL
|
||||
*
|
||||
* @param url URL from which a new record is constructed
|
||||
* @return a new service record instance
|
||||
*/
|
||||
public static ServiceRecordImpl createServiceRecord(String url) {
|
||||
return createServiceRecord(null, new BluetoothUrl(url), 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensures that the service record is valid.
|
||||
*
|
||||
* @throws ServiceRegistrationException in case described in the
|
||||
* JSR specification
|
||||
*/
|
||||
protected void checkServiceRecord() throws ServiceRegistrationException {
|
||||
synchronized (serviceRec) {
|
||||
// check if the ServiceClassIDList is not missing
|
||||
if (serviceRec.getAttributeValue(
|
||||
ServiceRecordImpl.SERVICE_CLASS_ATTR_ID) == null) {
|
||||
throw new ServiceRegistrationException(
|
||||
"ServiceClassIDList is missing.");
|
||||
}
|
||||
// check the ProtocolList is not missed
|
||||
DataElement protocolList = serviceRec.getAttributeValue(
|
||||
ServiceRecordImpl.PROTOCOL_DESCRIPTOR_LIST);
|
||||
if (protocolList == null) {
|
||||
throw new ServiceRegistrationException(
|
||||
"ProtocolDescriptorList is missing.");
|
||||
}
|
||||
Enumeration protocolListEnum =
|
||||
(Enumeration)protocolList.getValue();
|
||||
boolean l2cap = false;
|
||||
boolean rfcomm = false;
|
||||
while (protocolListEnum.hasMoreElements()) {
|
||||
Enumeration protocolEnum = (Enumeration)((DataElement)
|
||||
protocolListEnum.nextElement()).getValue();
|
||||
// check that L2CAP/RFCOMM UUIDs are not missing, check the CN
|
||||
// is not missing and the value has not changed
|
||||
while (protocolEnum.hasMoreElements()) {
|
||||
DataElement element = (DataElement)protocolEnum.
|
||||
nextElement();
|
||||
if (compareDataElements(element, L2CAP_UUID)) {
|
||||
l2cap = true;
|
||||
}
|
||||
if (compareDataElements(element, RFCOMM_UUID)) {
|
||||
rfcomm = true;
|
||||
if (CHANNEL_ID != null) {
|
||||
if (!protocolEnum.hasMoreElements() ||
|
||||
!compareDataElements((DataElement)protocolEnum.
|
||||
nextElement(), CHANNEL_ID)) {
|
||||
throw new ServiceRegistrationException(
|
||||
"Channel value has changed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (l2cap && rfcomm) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new ServiceRegistrationException("L2CAP UUID is missing.");
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensures that this notifier can accept connections.
|
||||
*
|
||||
* @throws IOException if notifier is closed or device is not
|
||||
* in connectable mode
|
||||
* @throws ServiceRegistrationException if the service record is not valid
|
||||
*/
|
||||
private void ensureConnectable() throws IOException {
|
||||
if (isClosed) {
|
||||
throw new BluetoothConnectionException(
|
||||
BluetoothConnectionException.FAILED_NOINFO,
|
||||
"Notifier is closed.");
|
||||
}
|
||||
if (!BCC.getInstance().isConnectable()) {
|
||||
throw new BluetoothStateException("The device is not connectable.");
|
||||
}
|
||||
checkServiceRecord();
|
||||
}
|
||||
|
||||
/*
|
||||
* Accepts client connection to the service this notifier is assigned to.
|
||||
* Adds corresponding service record to the SDDB, blocks until a successfull
|
||||
* connection to a client is established, retrieves the connection.
|
||||
*
|
||||
* @return bi-directional connection to a client just accepted.
|
||||
*
|
||||
* @throws IOException if notifier is closed or device is not
|
||||
* in connectable mode.
|
||||
*/
|
||||
public StreamConnection acceptAndOpen()
|
||||
throws IOException, ServiceRegistrationException {
|
||||
ensureConnectable();
|
||||
|
||||
// switch on listen mode if it has not been done yet
|
||||
doListen();
|
||||
|
||||
// adds the record only if is not yet in SDDB
|
||||
SDDB.getInstance().updateServiceRecord(serviceRec);
|
||||
|
||||
StreamConnection client;
|
||||
do {
|
||||
ensureConnectable();
|
||||
|
||||
// accept incoming connections if any
|
||||
client = doAccept();
|
||||
} while (client == null);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
/*
|
||||
* Closes this notifier making corresponding service record inaccessible.
|
||||
* updates the information on the communication server.
|
||||
*
|
||||
* @throws IOException if an error occured lower in Bluetooth stack.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
isClosed = true;
|
||||
|
||||
SDDB.getInstance().removeServiceRecord(serviceRec);
|
||||
|
||||
doClose();
|
||||
}
|
||||
|
||||
/*
|
||||
* Force listen mode by calling underlying stack methods.
|
||||
*
|
||||
* @throws IOException if an error occured
|
||||
*/
|
||||
|
||||
private void doListen() throws IOException {
|
||||
// force listening if it had not been done yet
|
||||
if (!isListenMode) {
|
||||
listen0();
|
||||
|
||||
isListenMode = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Force Bluetooth stack to listen for incoming client connections.
|
||||
*
|
||||
* Note: the method gets native connection handle directly from
|
||||
* <code>handle<code> field of <code>BTSPPNotifierImpl</code> object.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
private native void listen0() throws IOException;
|
||||
|
||||
/*
|
||||
* Advertises acception by calling underlying stack methods.
|
||||
*
|
||||
* @return BTSPPConnection instance to work with accepted client
|
||||
*/
|
||||
private StreamConnection doAccept() throws IOException {
|
||||
if (!isListenMode) {
|
||||
throw new BluetoothStateException("Device is not in listen mode");
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: native handle is set to peerHandleID field directly
|
||||
* by accept0 method and retrieved by L2CAPConnectionImpl constructor.
|
||||
*/
|
||||
accept0();
|
||||
|
||||
return new BTSPPConnectionImpl(url, mode, this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Accepts incoming client connection request.
|
||||
*
|
||||
* Note: the method gets native connection handle directly from
|
||||
* <code>handle<code> field of <code>BTSPPNotifierImpl</code> object.
|
||||
*
|
||||
* Note: new native connection handle to work with accepted incoming
|
||||
* client connection is setted directly to <code>handle</code> field of
|
||||
* appropriate <code>BTSPPConnectionImpl</code> object.
|
||||
*
|
||||
* @return <code>0</code> if incoming client connection was successfully
|
||||
* accepted;
|
||||
* <code>-1</code> if there is no pending incoming connections
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
protected native int accept0() throws IOException;
|
||||
|
||||
|
||||
/*
|
||||
* Closes this notifier at native layer.
|
||||
*/
|
||||
private void doClose() throws IOException {
|
||||
close0();
|
||||
}
|
||||
|
||||
/*
|
||||
* Closes this server connection.
|
||||
* Releases all native resources (such as sockets) owned by this notifier.
|
||||
*
|
||||
* Note: the method gets native connection handle directly from
|
||||
* <code>handle<code> field of <code>BTSPPNotifierImpl</code> object.
|
||||
*
|
||||
* @throws IOException IOException if an I/O error occurs
|
||||
*/
|
||||
private native void close0() throws IOException;
|
||||
|
||||
|
||||
/*
|
||||
* Creates an instanse of server connection at native layer.
|
||||
*
|
||||
* @param url <code>BluetoothUrl</code> that represents server
|
||||
* connection string to create notifier for
|
||||
* @return selected channel number to listen for incoming connections
|
||||
*/
|
||||
private int doCreate(BluetoothUrl url) throws IOException {
|
||||
return create0(url.authenticate, url.authorize,
|
||||
url.encrypt, url.master);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a server connection.
|
||||
*
|
||||
* Note: the method sets native connection handle directly to
|
||||
* <code>handle<code> field of <code>BTSPPNotifierImpl</code> object.
|
||||
*
|
||||
* @param auth <code>true</code> if authication is required
|
||||
* @param authz <code>true</code> if authorization 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
|
||||
* @return selected channel number to listen for incoming connections on
|
||||
* @throws IOException IOException if an I/O error occurs
|
||||
*/
|
||||
private native int create0(boolean auth, boolean authz,
|
||||
boolean enc, boolean master) throws IOException;
|
||||
|
||||
}
|
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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.obex;
|
||||
|
||||
import javax.obex.HeaderSet;
|
||||
import java.io.IOException;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Calendar;
|
||||
import java.util.Vector;
|
||||
|
||||
public class HeaderSetImpl implements HeaderSet {
|
||||
|
||||
/* Debug information, should be false for RR. */
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
public static final int OWNER_SERVER = 1;
|
||||
public static final int OWNER_CLIENT = 2;
|
||||
public static final int OWNER_SERVER_USER = 3;
|
||||
public static final int OWNER_CLIENT_USER = 4;
|
||||
|
||||
static final int TYPE_UNICODE = 0;
|
||||
static final int TYPE_BYTEARRAY = 1;
|
||||
static final int TYPE_BYTE = 2;
|
||||
static final int TYPE_LONG = 3;
|
||||
static final int TYPE_SPECIAL_TIME_4 = 4;
|
||||
static final int TYPE_SPECIAL_TIME_ISO = 5;
|
||||
static final int TYPE_SPECIAL_TYPE = 6;
|
||||
static final int TYPE_UNSUPPORTED = 7;
|
||||
static final int TYPE_AUTH_CHALLENGE = 8;
|
||||
static final int TYPE_AUTH_RESPONSE = 9;
|
||||
|
||||
/* package protected */ int owner;
|
||||
/* package protected */ int packetType;
|
||||
|
||||
private Hashtable headers;
|
||||
Vector challenges = new Vector();
|
||||
|
||||
public HeaderSetImpl(int owner) {
|
||||
this.owner = owner;
|
||||
headers = new Hashtable(5);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Adds headers from h to this HeaderSet.
|
||||
*/
|
||||
void merge(HeaderSetImpl h) {
|
||||
int[] idList = h.getHeaderList();
|
||||
|
||||
if (idList == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < idList.length; i++) {
|
||||
int id = idList[i];
|
||||
Object val = h.getHeader(id);
|
||||
int type = internalType(id);
|
||||
switch (type) {
|
||||
case TYPE_UNICODE:
|
||||
case TYPE_BYTE:
|
||||
case TYPE_LONG:
|
||||
case TYPE_SPECIAL_TYPE:
|
||||
setHeader(id, val);
|
||||
break;
|
||||
|
||||
case TYPE_BYTEARRAY:
|
||||
byte[] array = (byte[]) val;
|
||||
byte[] copy = new byte[array.length];
|
||||
System.arraycopy(array, 0, copy, 0, array.length);
|
||||
setHeader(id, (Object) copy);
|
||||
break;
|
||||
|
||||
case TYPE_SPECIAL_TIME_4:
|
||||
case TYPE_SPECIAL_TIME_ISO:
|
||||
Calendar cal = (Calendar) val;
|
||||
Calendar calCopy = Calendar.getInstance();
|
||||
calCopy.setTime(cal.getTime());
|
||||
setHeader(id, (Object) calCopy);
|
||||
break;
|
||||
|
||||
default:
|
||||
// no default
|
||||
if (DEBUG) {
|
||||
System.out.println("Debug: unknown header id");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Makes private copy of the headers.
|
||||
*/
|
||||
HeaderSetImpl(HeaderSetImpl h) {
|
||||
this.packetType = h.packetType;
|
||||
this.owner = h.owner;
|
||||
headers = new Hashtable(5);
|
||||
merge(h);
|
||||
}
|
||||
|
||||
boolean isSendable() {
|
||||
return owner >= OWNER_SERVER_USER;
|
||||
}
|
||||
|
||||
// interface functions:
|
||||
public void createAuthenticationChallenge(java.lang.String realm,
|
||||
boolean userID, boolean access) {
|
||||
|
||||
ObexAuth auth = ObexAuth.createChallenge(realm, userID, access);
|
||||
challenges.addElement(auth);
|
||||
}
|
||||
|
||||
public Object getHeader(int headerID) {
|
||||
Object value = headers.get(new Integer(headerID));
|
||||
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// check validness of headerID
|
||||
int type = internalType(headerID);
|
||||
|
||||
if (type >= TYPE_UNSUPPORTED) {
|
||||
throw new IllegalArgumentException("bad header id");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int[] getHeaderList() {
|
||||
synchronized (headers) {
|
||||
if (headers.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Enumeration keys = headers.keys();
|
||||
int[] ids = new int[headers.size()];
|
||||
int i = 0;
|
||||
|
||||
while (keys.hasMoreElements()) {
|
||||
Object obj = keys.nextElement();
|
||||
ids[i++] = ((Integer)obj).intValue();
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
}
|
||||
|
||||
public int getResponseCode() throws IOException {
|
||||
if (owner != OWNER_CLIENT) {
|
||||
throw new IOException("invalid use");
|
||||
}
|
||||
if (packetType == ObexPacketStream.OPCODE_CONTINUE) {
|
||||
throw new IOException("operation not finished");
|
||||
}
|
||||
return packetType;
|
||||
}
|
||||
|
||||
public void setHeader(int headerID, Object headerValue) {
|
||||
int type = internalType(headerID);
|
||||
String errormsg = "incompatible header object";
|
||||
|
||||
if (type >= TYPE_UNSUPPORTED) {
|
||||
throw new IllegalArgumentException("bad header id");
|
||||
}
|
||||
|
||||
if (headerValue == null) {
|
||||
synchronized (headers) {
|
||||
headers.remove(new Integer(headerID));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
boolean fail = false;
|
||||
switch (type) {
|
||||
case TYPE_UNICODE:
|
||||
if (!(headerValue instanceof String)) fail = true;
|
||||
break;
|
||||
|
||||
case TYPE_BYTEARRAY:
|
||||
if (!(headerValue instanceof byte[])) fail = true;
|
||||
break;
|
||||
|
||||
case TYPE_BYTE:
|
||||
if (!(headerValue instanceof Byte)) fail = true;
|
||||
break;
|
||||
|
||||
case TYPE_LONG:
|
||||
if (!(headerValue instanceof Long)) {
|
||||
fail = true;
|
||||
break;
|
||||
}
|
||||
long value = ((Long)headerValue).longValue();
|
||||
|
||||
if (value < 0L || value > 0xFFFFFFFFL) {
|
||||
errormsg = "long value out of range 0 .. 0xFFFFFFFF";
|
||||
fail = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TYPE_SPECIAL_TIME_4:
|
||||
if (!(headerValue instanceof Calendar)) fail = true;
|
||||
break;
|
||||
|
||||
case TYPE_SPECIAL_TIME_ISO:
|
||||
if (!(headerValue instanceof Calendar)) fail = true;
|
||||
break;
|
||||
|
||||
case TYPE_SPECIAL_TYPE:
|
||||
if (!(headerValue instanceof String)) fail = true;
|
||||
break;
|
||||
// no default
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
throw new IllegalArgumentException(errormsg);
|
||||
}
|
||||
synchronized (headers) {
|
||||
headers.put(new Integer(headerID), headerValue);
|
||||
}
|
||||
}
|
||||
|
||||
static final int internalType(int headerID) {
|
||||
if ((headerID & ~0xFF) != 0) {
|
||||
return TYPE_UNSUPPORTED;
|
||||
}
|
||||
if ((headerID & 0x30) == 0x30) {
|
||||
// user defined
|
||||
return headerID >> 6;
|
||||
}
|
||||
|
||||
switch (headerID) {
|
||||
case HeaderSet.TIME_ISO_8601:
|
||||
return TYPE_SPECIAL_TIME_ISO;
|
||||
|
||||
case HeaderSet.TIME_4_BYTE:
|
||||
return TYPE_SPECIAL_TIME_4;
|
||||
|
||||
case HeaderSet.TYPE:
|
||||
return TYPE_SPECIAL_TYPE;
|
||||
|
||||
case HeaderSet.COUNT:
|
||||
case HeaderSet.LENGTH:
|
||||
return TYPE_LONG;
|
||||
|
||||
case HeaderSet.NAME:
|
||||
case HeaderSet.DESCRIPTION:
|
||||
return TYPE_UNICODE;
|
||||
|
||||
case HeaderSet.TARGET:
|
||||
case HeaderSet.HTTP:
|
||||
case HeaderSet.WHO:
|
||||
case HeaderSet.OBJECT_CLASS:
|
||||
case HeaderSet.APPLICATION_PARAMETER:
|
||||
return TYPE_BYTEARRAY;
|
||||
|
||||
case ObexPacketStream.HEADER_AUTH_CHALLENGE:
|
||||
return TYPE_AUTH_CHALLENGE;
|
||||
|
||||
case ObexPacketStream.HEADER_AUTH_RESPONSE:
|
||||
return TYPE_AUTH_RESPONSE;
|
||||
|
||||
default:
|
||||
return TYPE_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,515 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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.obex;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Vector;
|
||||
import javax.obex.Authenticator;
|
||||
import javax.obex.PasswordAuthentication;
|
||||
|
||||
/*
|
||||
* Obex protocol authentication functinality.
|
||||
*/
|
||||
class ObexAuth {
|
||||
|
||||
/* Debug information, should be false for RR. */
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static byte[] column = { (byte)':' };
|
||||
|
||||
String realm;
|
||||
boolean userID;
|
||||
boolean access;
|
||||
byte[] nonce;
|
||||
private static int counter = 0;
|
||||
|
||||
// used in prepareChallenge, addChallenge
|
||||
private byte[] realm_array;
|
||||
private int challengeLength;
|
||||
|
||||
static ObexAuth createChallenge(String realm,
|
||||
boolean userID, boolean access) {
|
||||
return new ObexAuth(realm, null, userID, access);
|
||||
}
|
||||
|
||||
private ObexAuth(String realm, byte[] nonce, boolean userID,
|
||||
boolean access) {
|
||||
this.realm = realm;
|
||||
this.nonce = nonce;
|
||||
this.userID = userID;
|
||||
this.access = access;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare challenge before adding to packet.
|
||||
* @return length of challenge
|
||||
*/
|
||||
int prepareChallenge() {
|
||||
if (challengeLength != 0) {
|
||||
return challengeLength;
|
||||
}
|
||||
try {
|
||||
int len = 24;
|
||||
realm_array = null;
|
||||
if (realm != null) {
|
||||
realm_array = realm.getBytes("UTF-16BE");
|
||||
len += 3 + realm_array.length;
|
||||
}
|
||||
challengeLength = len;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
if (DEBUG) {
|
||||
System.out.println("prepareChallenge(): ERROR, no encoding");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return challengeLength;
|
||||
}
|
||||
|
||||
int addChallenge(byte[] packet, int offset) throws IOException {
|
||||
int len = prepareChallenge();
|
||||
|
||||
packet[offset] = (byte)ObexPacketStream.HEADER_AUTH_CHALLENGE;
|
||||
packet[offset+1] = (byte) (len >> 8);
|
||||
packet[offset+2] = (byte) (len & 255);
|
||||
packet[offset+3] = (byte) 0; // nonce tag (0x0)
|
||||
packet[offset+4] = (byte) 16; // nonce len (16) (1 bytes)
|
||||
nonce = makeNonce(); // 16 byte of nonce
|
||||
if (DEBUG) {
|
||||
print("addChallenge: nonce", nonce);
|
||||
}
|
||||
System.arraycopy(nonce, 0, packet, offset + 5, 16);
|
||||
packet[offset+21] = (byte) 1; // options tag (0x1)
|
||||
packet[offset+22] = (byte) 1; // options length (1)
|
||||
packet[offset+23] = (byte) ((userID ? 1 : 0) + (access ? 0 : 2));
|
||||
if (realm != null) {
|
||||
int realm_len = realm_array.length;
|
||||
packet[offset+24] = (byte) 2; // realm tag (0x2)
|
||||
|
||||
// realm length including encoding (1 byte)
|
||||
packet[offset+25] = (byte) (realm_len + 1);
|
||||
packet[offset+26] = (byte) 0xFF; // realm encoding UNICODE
|
||||
System.arraycopy(realm_array, 0, packet, offset+27, realm_len);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
private static byte[] makeNonce() throws IOException {
|
||||
SSLWrapper md5 = new SSLWrapper();
|
||||
byte[] timestamp = createTimestamp();
|
||||
md5.update(timestamp, 0, timestamp.length);
|
||||
md5.update(column, 0, 1);
|
||||
byte[] privateKey = getPrivateKey();
|
||||
byte[] nonce = new byte[16];
|
||||
md5.doFinal(privateKey, 0, privateKey.length, nonce, 0);
|
||||
return nonce;
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates timestamp.
|
||||
* No strict specification for timestamp generation in OBEX 1.2
|
||||
* @return timestamp value
|
||||
*/
|
||||
private static byte[] createTimestamp() {
|
||||
long time = System.currentTimeMillis();
|
||||
byte[] timestamp = new byte[9];
|
||||
timestamp[0] = (byte)(time >> 56);
|
||||
timestamp[1] = (byte)(time >> 48);
|
||||
timestamp[2] = (byte)(time >> 40);
|
||||
timestamp[3] = (byte)(time >> 32);
|
||||
timestamp[4] = (byte)(time >> 24);
|
||||
timestamp[5] = (byte)(time >> 16);
|
||||
timestamp[6] = (byte)(time >> 8);
|
||||
timestamp[7] = (byte)(time);
|
||||
|
||||
synchronized (ObexAuth.class) {
|
||||
timestamp[8] = (byte)(counter++);
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
private static byte[] privateKey = null;
|
||||
|
||||
/*
|
||||
* Create and return private key.
|
||||
* Weak security scheme. Should be rewritten for more secure
|
||||
* implementation if used outside emulator.
|
||||
*/
|
||||
private synchronized static byte[] getPrivateKey() throws IOException {
|
||||
if (privateKey != null) {
|
||||
return privateKey;
|
||||
}
|
||||
SSLWrapper md5 = new SSLWrapper();
|
||||
byte[] keyData = null;
|
||||
|
||||
try {
|
||||
keyData = "timestamp = ".getBytes("ISO-8859-1");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
md5.update(keyData, 0, keyData.length);
|
||||
byte[] timestamp = createTimestamp();
|
||||
privateKey = new byte[16];
|
||||
md5.doFinal(timestamp, 0, timestamp.length, privateKey, 0);
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
static void makeDigest(byte[] buffer, int offset, byte[] nonce,
|
||||
byte[] password)
|
||||
throws IOException {
|
||||
SSLWrapper md5 = new SSLWrapper();
|
||||
md5.update(nonce, 0, 16);
|
||||
md5.update(column, 0, 1);
|
||||
md5.doFinal(password, 0, password.length, buffer, offset);
|
||||
}
|
||||
|
||||
static ObexAuth parseAuthChallenge(byte[] buffer, int packetOffset,
|
||||
int length)
|
||||
throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.println("ObexAuth.parseAuthChallenge()");
|
||||
}
|
||||
|
||||
// default values
|
||||
boolean readonly = false;
|
||||
boolean needUserid = false;
|
||||
byte[] nonce = null;
|
||||
String realm = null;
|
||||
|
||||
// skiping header type and length
|
||||
int offset = packetOffset + 3;
|
||||
length += packetOffset;
|
||||
|
||||
// decoding data in buffer
|
||||
while (offset < length) {
|
||||
int tag = buffer[offset] & 0xFF;
|
||||
int len = buffer[offset + 1] & 0xFF;
|
||||
offset += 2;
|
||||
|
||||
|
||||
switch (tag) {
|
||||
case 0x0: // nonce
|
||||
if (len != 16 || nonce != null) {
|
||||
throw new IOException("protocol error");
|
||||
}
|
||||
nonce = new byte[16];
|
||||
System.arraycopy(buffer, offset, nonce, 0, 16);
|
||||
if (DEBUG) {
|
||||
print("got challenge: nonce", nonce);
|
||||
}
|
||||
break;
|
||||
case 0x1: // options
|
||||
if (len != 1) {
|
||||
throw new IOException("protocol error");
|
||||
}
|
||||
int options = buffer[offset];
|
||||
readonly = ((options & 2) != 0);
|
||||
needUserid = ((options & 1) != 0);
|
||||
break;
|
||||
case 0x2: // realm
|
||||
try {
|
||||
int encodingID = buffer[offset] & 0xFF;
|
||||
String encoding = null;
|
||||
if (encodingID == 255) encoding = "UTF-16BE";
|
||||
else if (encodingID == 0) encoding = "US-ASCII";
|
||||
else if (encodingID < 10) encoding = "ISO-8859-"
|
||||
+ encoding;
|
||||
else throw new UnsupportedEncodingException();
|
||||
|
||||
realm = new String(buffer, offset + 1,
|
||||
len - 1, encoding);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// already: realm = null;
|
||||
}
|
||||
}
|
||||
offset += len;
|
||||
}
|
||||
if (offset != length) {
|
||||
throw new IOException("protocol error");
|
||||
}
|
||||
return new ObexAuth(realm, nonce, needUserid, !readonly);
|
||||
}
|
||||
|
||||
int replyAuthChallenge(byte[] buffer, int packetOffset,
|
||||
Authenticator authenticator) throws IOException {
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("ObexAuth.replyAuthChallenge()");
|
||||
}
|
||||
|
||||
if (realm == null) {
|
||||
realm = "";
|
||||
}
|
||||
|
||||
byte[] password = null;
|
||||
byte[] username = null;
|
||||
|
||||
try {
|
||||
PasswordAuthentication pass =
|
||||
authenticator.onAuthenticationChallenge(realm, userID, access);
|
||||
|
||||
password = pass.getPassword();
|
||||
int uidLen = 0;
|
||||
// userid subheader length with subheader <tag> and <len>
|
||||
|
||||
username = pass.getUserName();
|
||||
if (userID || username != null) {
|
||||
|
||||
// username is required but not provided
|
||||
if (userID && username.length == 0) {
|
||||
if (DEBUG) {
|
||||
System.out.println("ObexAuth.replyAuthChallenge():"
|
||||
+ " required username not provided");
|
||||
}
|
||||
throw new Exception();
|
||||
}
|
||||
uidLen = 2 + username.length;
|
||||
|
||||
// maximum supported username length = 20
|
||||
if (uidLen > 22) uidLen = 22;
|
||||
}
|
||||
|
||||
int len = 39 + uidLen;
|
||||
// byte[] response = new byte[len];
|
||||
buffer[packetOffset + 0] = (byte)ObexPacketStream
|
||||
.HEADER_AUTH_RESPONSE;
|
||||
buffer[packetOffset + 1] = (byte) (len >> 8);
|
||||
buffer[packetOffset + 2] = (byte) (len & 255);
|
||||
buffer[packetOffset + 3] = 0x0; // tag (Request-Digest)
|
||||
buffer[packetOffset + 4] = 16; // digest len (16)
|
||||
makeDigest(buffer, packetOffset + 5, nonce, password);
|
||||
buffer[packetOffset + 21] = 0x02; // tag nonce
|
||||
buffer[packetOffset + 22] = 16; // nonce len (16)
|
||||
System.arraycopy(nonce, 0, buffer, packetOffset + 23, 16);
|
||||
if (DEBUG) {
|
||||
print("send response: nonce", nonce);
|
||||
}
|
||||
if (uidLen > 2) {
|
||||
buffer[packetOffset + 39] = 0x01; // tag userid
|
||||
buffer[packetOffset + 40] = (byte) (uidLen - 2); // userid len
|
||||
System.arraycopy(username, 0, buffer, packetOffset + 41,
|
||||
uidLen - 2);
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("ObexAuth.replyAuthChallenge():"
|
||||
+ " response generated");
|
||||
}
|
||||
return len;
|
||||
|
||||
// need to create authentication response
|
||||
// we should resend previous packet with the authentication response
|
||||
|
||||
} catch (Throwable t) {
|
||||
if (DEBUG) {
|
||||
System.out.println("ObexAuth.replyAuthChallenge(): exception");
|
||||
t.printStackTrace();
|
||||
}
|
||||
// will caught NullPointerException if authenticator
|
||||
// was not provided
|
||||
// will caught exceptions in handler
|
||||
// will caught NullPointerException exception
|
||||
// if username == null and needUserid
|
||||
//
|
||||
// wrong response from client application,
|
||||
// ignoring authentication challenge
|
||||
// client will receive UNAUTHORIZED
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void print(String msg, byte[] array) {
|
||||
if (DEBUG) {
|
||||
System.out.println(msg);
|
||||
if (array == null) {
|
||||
System.out.println("[0] = NULL");
|
||||
return;
|
||||
}
|
||||
System.out.print("[" + array.length + "]");
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
System.out.print(" " + Integer.toHexString(array[i] & 0xFF));
|
||||
}
|
||||
System.out.println("");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean compare(byte[] src1, byte[] src2) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (src1[i] != src2[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean checkAuthResponse(byte[] buffer, int packetOffset,
|
||||
int length, ObexPacketStream stream, Vector challenges)
|
||||
throws IOException {
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("ObexAuth.parseAuthResponse()");
|
||||
}
|
||||
|
||||
// skiping header type and length
|
||||
int offset = packetOffset + 3;
|
||||
length += packetOffset;
|
||||
|
||||
byte[] digest = null;
|
||||
byte[] username = null;
|
||||
byte[] nonce = null;
|
||||
|
||||
// decoding data in buffer
|
||||
while (offset < length) {
|
||||
int tag = buffer[offset] & 0xFF;
|
||||
int len = buffer[offset + 1] & 0xFF;
|
||||
offset += 2;
|
||||
|
||||
switch (tag) {
|
||||
case 0x0: // digest
|
||||
if (DEBUG) {
|
||||
System.out.println("got digest");
|
||||
}
|
||||
if (len != 16 || digest != null) {
|
||||
throw new IOException("protocol error (1)");
|
||||
}
|
||||
digest = new byte[16];
|
||||
System.arraycopy(buffer, offset, digest, 0, 16);
|
||||
if (DEBUG) {
|
||||
print("got response: digest", digest);
|
||||
}
|
||||
break;
|
||||
case 0x1: // username
|
||||
if (DEBUG) {
|
||||
System.out.println("got username");
|
||||
}
|
||||
if (len > 20 || len == 0 || username != null) {
|
||||
throw new IOException("protocol error (2)");
|
||||
}
|
||||
username = new byte[len];
|
||||
System.arraycopy(buffer, offset, username, 0, len);
|
||||
break;
|
||||
case 0x2: // nonce
|
||||
if (DEBUG) {
|
||||
System.out.println("got nonce");
|
||||
}
|
||||
if (len != 16 || nonce != null) {
|
||||
throw new IOException("protocol error (3)");
|
||||
}
|
||||
nonce = new byte[16];
|
||||
System.arraycopy(buffer, offset, nonce, 0, 16);
|
||||
if (DEBUG) {
|
||||
print("got response: nonce", nonce);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (DEBUG) {
|
||||
System.out.println("unknown tag = " + tag);
|
||||
}
|
||||
}
|
||||
offset += len;
|
||||
}
|
||||
|
||||
if (offset != length) {
|
||||
throw new IOException("protocol error (4)");
|
||||
}
|
||||
|
||||
|
||||
// check nonce and select auth object
|
||||
ObexAuth auth = null;
|
||||
|
||||
if (nonce == null) {
|
||||
if (DEBUG) {
|
||||
System.out.println("no nonce received, using first auth");
|
||||
}
|
||||
if (challenges.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
auth = (ObexAuth) challenges.elementAt(0);
|
||||
nonce = auth.nonce;
|
||||
challenges.removeElementAt(0);
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
System.out.println("nonce provided, searching for auth");
|
||||
System.out.println("challenges = " + challenges.size());
|
||||
}
|
||||
for (int i = 0; i < challenges.size(); i++) {
|
||||
ObexAuth a = (ObexAuth) challenges.elementAt(i);
|
||||
if (compare(nonce, a.nonce)) {
|
||||
if (DEBUG) {
|
||||
System.out.println("nonce is in " + i + " challenge");
|
||||
}
|
||||
auth = a;
|
||||
challenges.removeElementAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (DEBUG) {
|
||||
System.out.println("auth = " + auth);
|
||||
}
|
||||
if (auth == null)
|
||||
return false;
|
||||
}
|
||||
|
||||
// check username existance
|
||||
if (auth.userID && username == null) {
|
||||
if (DEBUG) {
|
||||
System.out.println("need username!");
|
||||
}
|
||||
// NOTE: may be too strict
|
||||
stream.onAuthenticationFailure(username);
|
||||
return false;
|
||||
}
|
||||
|
||||
// ask password from authenticator and check digest
|
||||
try {
|
||||
if (DEBUG) {
|
||||
System.out.println("running onAuthenticationResponse()...");
|
||||
}
|
||||
byte[] password =
|
||||
stream.authenticator.onAuthenticationResponse(username);
|
||||
byte[] localDigest = new byte[16];
|
||||
makeDigest(localDigest, 0, nonce, password);
|
||||
if (DEBUG) {
|
||||
System.out.println("digest created");
|
||||
}
|
||||
boolean res = compare(localDigest, digest);
|
||||
|
||||
if (res == false) {
|
||||
if (DEBUG) {
|
||||
System.out.println("Calling onAuthenticationFailure()..");
|
||||
}
|
||||
stream.onAuthenticationFailure(username);
|
||||
}
|
||||
return res;
|
||||
} catch (Throwable t) {
|
||||
// catch localDigest = null, crypto and user code exception
|
||||
if (DEBUG) {
|
||||
System.out.println("exception");
|
||||
}
|
||||
stream.onAuthenticationFailure(username);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,922 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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.obex;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
import java.util.Vector;
|
||||
import java.util.Stack;
|
||||
import javax.microedition.io.Connection;
|
||||
import javax.obex.ResponseCodes;
|
||||
import javax.obex.Authenticator;
|
||||
import com.sun.j2me.log.Logging;
|
||||
|
||||
/*
|
||||
* Obex core protocol.
|
||||
*/
|
||||
public abstract class ObexPacketStream implements Connection {
|
||||
|
||||
/* Debug information, should be false for RR. */
|
||||
private static final boolean DEBUG = false;
|
||||
private static final boolean DEBUG2 = false;
|
||||
|
||||
// OBEX operations opcodes
|
||||
static final int OPCODE_CONNECT = 0x80;
|
||||
static final int OPCODE_DISCONNECT = 0x81;
|
||||
static final int OPCODE_PUT = 0x02;
|
||||
static final int OPCODE_GET = 0x03;
|
||||
static final int OPCODE_SETPATH = 0x85;
|
||||
static final int OPCODE_CONTINUE = 0x90;
|
||||
static final int OPCODE_ABORT = 0xFF;
|
||||
static final int OPCODE_FINAL = 0x80;
|
||||
static final int OPCODE_GET_FINAL = OPCODE_GET | OPCODE_FINAL;
|
||||
|
||||
static final byte[] PACKET_ABORT = { (byte) OPCODE_ABORT, 0, 0 };
|
||||
static final byte[] PACKET_CONTINUE = { (byte) OPCODE_CONTINUE, 0, 0};
|
||||
static final byte[] PACKET_DISCONNECT = {
|
||||
(byte) OPCODE_DISCONNECT, 0, 0};
|
||||
static final byte[] PACKET_SUCCESS = {
|
||||
(byte) ResponseCodes.OBEX_HTTP_OK, 0, 0};
|
||||
|
||||
static final byte[] PACKET_BAD_REQUEST = {
|
||||
(byte) ResponseCodes.OBEX_HTTP_BAD_REQUEST, 0, 0};
|
||||
|
||||
static final byte[] PACKET_NOT_IMPLEMENTED = {
|
||||
(byte) ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED, 0, 0};
|
||||
|
||||
private static final int HEADER_BODY = 0x48;
|
||||
private static final int HEADER_EOFBODY = 0x49;
|
||||
private static final int HEADER_CONNECTION_ID = 0xCB;
|
||||
static final int HEADER_AUTH_CHALLENGE = 0x4D;
|
||||
static final int HEADER_AUTH_RESPONSE = 0x4E;
|
||||
|
||||
static TimeZone utcTimeZone = TimeZone.getTimeZone("UTC");
|
||||
|
||||
private static final int TRANSPORT_READ_INTERVAL = 10;
|
||||
|
||||
private ObexTransport transport;
|
||||
|
||||
Authenticator authenticator;
|
||||
|
||||
/*
|
||||
* Generated authentication responses. They will be send in sendPacket().
|
||||
* Stored in <CODE>byte[]</CODE> format.
|
||||
*/
|
||||
Vector authResponses = new Vector();
|
||||
|
||||
/*
|
||||
* Sent authentication challenges.
|
||||
* They will be used for check authentication responses.
|
||||
*/
|
||||
Vector authChallenges = new Vector();
|
||||
|
||||
/*
|
||||
* True when buffer contains packet for sending and some headers can
|
||||
* be added.
|
||||
*/
|
||||
boolean moreHeaders = false;
|
||||
|
||||
|
||||
/*
|
||||
* Outgoing packet contains authentication challenges, so it should be
|
||||
* sent immediatly.
|
||||
*/
|
||||
boolean challengesToSend = false;
|
||||
|
||||
/*
|
||||
* True if one of sending headers cannot feet in empty packet.
|
||||
*/
|
||||
boolean headerOverflow = false;
|
||||
|
||||
/*
|
||||
* True if sending packet contains target header.
|
||||
*/
|
||||
boolean containsTargetHeader = false;
|
||||
|
||||
/*
|
||||
* Queue of outgoing headers, not feet in packet.
|
||||
*/
|
||||
Vector queuedHeaders;
|
||||
QueuedHeader newHeader;
|
||||
Stack emptyHeadersPool;
|
||||
|
||||
/*
|
||||
* Set when sending auth challenge,
|
||||
* reset when received valid auth response in next
|
||||
* packet.
|
||||
*/
|
||||
boolean authFailed = false;
|
||||
|
||||
/*
|
||||
* True if this is ClientSession, false in ServerConnectionImpl.
|
||||
*/
|
||||
boolean isClient;
|
||||
|
||||
/*
|
||||
* Client is connected flag. Ignored by ServerConnectionImpl.
|
||||
*/
|
||||
boolean isConnected;
|
||||
|
||||
int OBEX_MAXIMUM_PACKET_LENGTH;
|
||||
|
||||
byte[] buffer, cache;
|
||||
int packetLength;
|
||||
int packetOffset;
|
||||
int packetType;
|
||||
|
||||
int maxSendLength;
|
||||
|
||||
boolean dataOpened, dataClosed;
|
||||
boolean isEof;
|
||||
int dataOffset;
|
||||
|
||||
/*
|
||||
* Connection id used in <code>setConnectionID</code>,
|
||||
* <code>getConnectioID</code>.
|
||||
*/
|
||||
|
||||
ObexPacketStream(ObexTransport transport) {
|
||||
this.transport = transport;
|
||||
OBEX_MAXIMUM_PACKET_LENGTH = transport.getMaximumPacketSize();
|
||||
buffer = new byte[OBEX_MAXIMUM_PACKET_LENGTH];
|
||||
cache = new byte[OBEX_MAXIMUM_PACKET_LENGTH];
|
||||
maxSendLength = OBEX_MAXIMUM_PACKET_LENGTH;
|
||||
newHeader = new QueuedHeader(this);
|
||||
queuedHeaders = new Vector();
|
||||
emptyHeadersPool = new Stack();
|
||||
}
|
||||
|
||||
|
||||
// interface visible function
|
||||
public void close() {
|
||||
try {
|
||||
if (transport != null) {
|
||||
transport.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// nothing
|
||||
}
|
||||
transport = null;
|
||||
}
|
||||
|
||||
public void setAuthenticator(Authenticator authenticator) {
|
||||
if (authenticator == null) {
|
||||
throw new NullPointerException("null authenticator");
|
||||
}
|
||||
this.authenticator = authenticator;
|
||||
}
|
||||
|
||||
public Connection getTransport()
|
||||
throws IOException {
|
||||
if (transport == null) {
|
||||
throw new IOException("connection error");
|
||||
}
|
||||
return transport.getUnderlyingConnection();
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets link broken flag.
|
||||
*/
|
||||
void brokenLink() {
|
||||
close();
|
||||
}
|
||||
|
||||
boolean isClosed() {
|
||||
return transport == null;
|
||||
}
|
||||
|
||||
void packetBegin(byte[] head) {
|
||||
if (DEBUG) {
|
||||
System.out.println("packetBegin()");
|
||||
}
|
||||
|
||||
containsTargetHeader = false;
|
||||
moreHeaders = true;
|
||||
challengesToSend = false;
|
||||
System.arraycopy(head, 0, buffer, 0, head.length);
|
||||
packetLength = head.length;
|
||||
authChallenges.removeAllElements();
|
||||
dataOpened = false;
|
||||
dataClosed = false;
|
||||
dataOffset = -3; // generate aoobe when accessed
|
||||
}
|
||||
|
||||
int packetAddData(byte[] data, int offset, int length) {
|
||||
if (DEBUG) {
|
||||
System.out.println("packetAddData()");
|
||||
}
|
||||
// preventing writing several data blocks, just in case
|
||||
if (dataClosed)
|
||||
return 0;
|
||||
|
||||
if (!dataOpened) {
|
||||
// let it be at least 3 bytes workload to create new Body header
|
||||
if (packetLength + 6 > maxSendLength) {
|
||||
return 0;
|
||||
}
|
||||
buffer[packetLength] = HEADER_BODY;
|
||||
dataOffset = packetLength;
|
||||
packetLength += 3;
|
||||
dataOpened = true;
|
||||
}
|
||||
|
||||
int len;
|
||||
if (packetLength + length > maxSendLength) {
|
||||
len = maxSendLength - packetLength;
|
||||
} else {
|
||||
len = length;
|
||||
}
|
||||
System.arraycopy(data, offset, buffer, packetLength, len);
|
||||
packetLength += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
int getPacketLength() {
|
||||
return packetLength;
|
||||
}
|
||||
|
||||
void restorePacketLength(int len) {
|
||||
packetLength = len;
|
||||
}
|
||||
|
||||
boolean packetEOFBody() {
|
||||
if (DEBUG) {
|
||||
System.out.println("packetEOFBody()");
|
||||
}
|
||||
if (dataClosed) {
|
||||
return false;
|
||||
}
|
||||
if (dataOpened) {
|
||||
buffer[dataOffset+0] = HEADER_EOFBODY;
|
||||
return true;
|
||||
} else {
|
||||
if (packetLength + 3 > maxSendLength) {
|
||||
return false;
|
||||
}
|
||||
buffer[packetLength++] = HEADER_EOFBODY;
|
||||
buffer[packetLength++] = 0; // length
|
||||
buffer[packetLength++] = 3;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void packetMarkFinal() {
|
||||
if (DEBUG) {
|
||||
System.out.println("packetMarkFinal()");
|
||||
}
|
||||
buffer[0] |= 0x80;
|
||||
}
|
||||
|
||||
void setPacketType(int type) {
|
||||
if (DEBUG) {
|
||||
System.out.println("setPacketType()");
|
||||
}
|
||||
buffer[0] = (byte) type;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finish packet and send it. Remove Connection ID header if packet also
|
||||
* contains TARGET header.
|
||||
*/
|
||||
void packetEndStripConnID() throws IOException {
|
||||
|
||||
// first header id is in 3 byte on all packet except CONNECT
|
||||
// and this function is known not to be called for connect() operation.
|
||||
if ((buffer[3] & 0xFF) == HeaderSetImpl.TARGET) {
|
||||
packetLength -= 5;
|
||||
|
||||
// length of Connection ID packet is 5 bytes:
|
||||
// 1 byte header + 4 byte int value
|
||||
for (int i = 3; i < packetLength; i++) {
|
||||
buffer[i] = buffer[i + 5];
|
||||
}
|
||||
}
|
||||
packetEnd();
|
||||
}
|
||||
|
||||
void packetEnd() throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.println("packetEnd()");
|
||||
}
|
||||
moreHeaders = false;
|
||||
|
||||
if (transport == null) {
|
||||
throw new IOException("connection error");
|
||||
}
|
||||
|
||||
if (dataOpened) {
|
||||
// closing Body header
|
||||
int len = packetLength - dataOffset;
|
||||
buffer[dataOffset+1] = (byte)(len >> 8);
|
||||
buffer[dataOffset+2] = (byte)len;
|
||||
dataOpened = false;
|
||||
dataClosed = true;
|
||||
}
|
||||
// update packet length field
|
||||
buffer[1] = (byte)(packetLength / 0x100);
|
||||
buffer[2] = (byte)(packetLength % 0x100);
|
||||
|
||||
if (DEBUG) {
|
||||
int len = packetLength;
|
||||
if (!DEBUG2 && len > 20) {
|
||||
len = 20;
|
||||
}
|
||||
System.out.println("send:");
|
||||
for (int i = 0; i < len; i++) {
|
||||
System.out.print(" 0x" + Integer.toHexString(buffer[i] & 0xFF));
|
||||
int chr = buffer[i] & 0xFF;
|
||||
if (chr >= 32 && chr < 128) {
|
||||
System.out.print("(" + (char)(buffer[i] & 0xFF) + ")");
|
||||
}
|
||||
}
|
||||
if (packetLength != len) {
|
||||
System.out.print("...");
|
||||
}
|
||||
System.out.println("");
|
||||
}
|
||||
try {
|
||||
transport.write(buffer, packetLength);
|
||||
} catch (IOException e) {
|
||||
brokenLink();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Connection Identifier:
|
||||
* * must be first header in packet
|
||||
* * 0xFFFFFFFF considered invalid - it is up to application
|
||||
* * can't be sent on connect() request
|
||||
* * can't be used with Target header in one packet
|
||||
*/
|
||||
final void packetAddConnectionID(long id, HeaderSetImpl headers) {
|
||||
// SPEC: Illegal to send a Connection Id and a Target header
|
||||
// in the same operation.
|
||||
if (headers != null
|
||||
&& headers.getHeader(HeaderSetImpl.TARGET) != null) {
|
||||
return;
|
||||
}
|
||||
if (id < 0L || id > 0xFFFFFFFFL) {
|
||||
return;
|
||||
}
|
||||
buffer[packetLength++] = (byte)HEADER_CONNECTION_ID;
|
||||
encodeInt(id);
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called to handle a situation than header is too large.
|
||||
* @throws IOException
|
||||
*/
|
||||
abstract void headerTooLarge() throws IOException;
|
||||
|
||||
/*
|
||||
* Adds the specified headers to the packet.
|
||||
*/
|
||||
final void packetAddHeaders(HeaderSetImpl headers)
|
||||
throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.println("packetAddHeaders()");
|
||||
}
|
||||
headerOverflow = false;
|
||||
newHeader.sendAllQueued();
|
||||
|
||||
if (headers == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int[] idList = headers.getHeaderList();
|
||||
|
||||
if (!headers.challenges.isEmpty()) {
|
||||
newHeader.sendOrQueue(HEADER_AUTH_CHALLENGE,
|
||||
headers.challenges);
|
||||
}
|
||||
|
||||
if (idList == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < idList.length; i++) {
|
||||
int id = idList[i];
|
||||
Object value = headers.getHeader(id);
|
||||
newHeader.sendOrQueue(id, value);
|
||||
}
|
||||
}
|
||||
|
||||
void packetAddAuthResponses() throws IOException {
|
||||
try {
|
||||
for (int i = 0; i < authResponses.size(); i++) {
|
||||
if (DEBUG) {
|
||||
System.out.println(
|
||||
"packetAddAuthResponses(): added response");
|
||||
}
|
||||
ObexAuth response = (ObexAuth) authResponses.elementAt(i);
|
||||
int len = response.replyAuthChallenge(buffer, packetLength,
|
||||
authenticator);
|
||||
packetLength += len;
|
||||
}
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
throw new IOException("auth response request too large");
|
||||
}
|
||||
if (packetLength > maxSendLength) {
|
||||
throw new IOException("auth response request too large");
|
||||
}
|
||||
}
|
||||
|
||||
final void sendPacket(byte[] head, long connectionId,
|
||||
HeaderSetImpl headers, boolean allHeaders) throws IOException {
|
||||
packetBegin(head);
|
||||
packetAddConnectionID(connectionId, headers);
|
||||
packetAddAuthResponses();
|
||||
packetAddHeaders(headers);
|
||||
if (allHeaders && !queuedHeaders.isEmpty()) {
|
||||
queuedHeaders.removeAllElements();
|
||||
throw new IOException("packet too large for peer");
|
||||
}
|
||||
packetEnd();
|
||||
}
|
||||
|
||||
private final void encodeInt(long val) {
|
||||
buffer[packetLength++] = (byte)(val >> 24);
|
||||
buffer[packetLength++] = (byte)(val >> 16);
|
||||
buffer[packetLength++] = (byte)(val >> 8);
|
||||
buffer[packetLength++] = (byte)val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads at least <code>length</code> bytes starting from the given offset
|
||||
* into the internal buffer. More than <code>length</code> bytes may be
|
||||
* actually read. The calling function must ensure the buffer is large
|
||||
* enough to store the entire packet.
|
||||
*
|
||||
* @param offset starting offset in the destination buffer
|
||||
* @param length minimum number of bytes to read
|
||||
* @return number of bytes actually read
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
private int readLeast(int offset, int length)
|
||||
throws IOException {
|
||||
if (transport == null) {
|
||||
throw new IOException("connection error");
|
||||
}
|
||||
int read = 0;
|
||||
while (read < length) {
|
||||
int count = transport.read(cache);
|
||||
System.arraycopy(cache, 0, buffer, offset + read, count);
|
||||
read += count;
|
||||
if (read < length) {
|
||||
try {
|
||||
Thread.sleep(TRANSPORT_READ_INTERVAL);
|
||||
} catch (InterruptedException e) {
|
||||
throw new InterruptedIOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
final void recvPacket() throws IOException {
|
||||
authResponses.removeAllElements();
|
||||
if (transport == null) {
|
||||
throw new IOException("connection error");
|
||||
}
|
||||
try {
|
||||
int read = readLeast(0, 3);
|
||||
packetType = buffer[0] & 0xff;
|
||||
packetLength = ((buffer[1] & 0xff) << 8) + (buffer[2] & 0xff);
|
||||
if (DEBUG) {
|
||||
Logging.report(Logging.INFORMATION, 0,
|
||||
"Expecting " + packetLength + " bytes to arrive...");
|
||||
}
|
||||
if (read < packetLength) {
|
||||
readLeast(read, packetLength - read);
|
||||
}
|
||||
|
||||
// dump packet:
|
||||
if (DEBUG) {
|
||||
int len = packetLength;
|
||||
if (!DEBUG2 && len > 20) {
|
||||
len = 20;
|
||||
}
|
||||
|
||||
System.out.println("recv: ");
|
||||
for (int i = 0; i < len; i++) {
|
||||
System.out.print(" 0x"
|
||||
+ Integer.toHexString(buffer[i] & 0xFF)
|
||||
.toUpperCase());
|
||||
}
|
||||
if (len != packetLength) System.out.print("...");
|
||||
System.out.println("");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
brokenLink();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private final void parseHeader(HeaderSetImpl headers)
|
||||
throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.println("parseHeader()");
|
||||
}
|
||||
try {
|
||||
int headerId = buffer[packetOffset++] & 0xff;
|
||||
int inputType = headerId >> 6;
|
||||
int outputType = HeaderSetImpl.internalType(headerId);
|
||||
if (outputType != HeaderSetImpl.TYPE_UNSUPPORTED) {
|
||||
inputType = outputType;
|
||||
}
|
||||
|
||||
Object result = null;
|
||||
|
||||
switch (inputType) {
|
||||
// ids which require special handling
|
||||
case HeaderSetImpl.TYPE_SPECIAL_TIME_ISO:
|
||||
try {
|
||||
result = decodeTime8601();
|
||||
} catch (IOException e) {
|
||||
// IMPL_NOTE: Got invalid time header,
|
||||
// should probably just ignore it.
|
||||
}
|
||||
break;
|
||||
|
||||
case HeaderSetImpl.TYPE_SPECIAL_TIME_4:
|
||||
long date = decodeInt();
|
||||
Calendar cal = Calendar.getInstance(utcTimeZone);
|
||||
cal.setTime(new Date(date * 1000L));
|
||||
result = cal;
|
||||
packetOffset += 4;
|
||||
break;
|
||||
|
||||
case HeaderSetImpl.TYPE_SPECIAL_TYPE:
|
||||
int len = decodeLength16(packetOffset) - 3;
|
||||
packetOffset += 2;
|
||||
if (buffer[packetOffset + len - 1] != 0) {
|
||||
throw new IOException(
|
||||
"protocol error, "
|
||||
+ "type field not null terminated");
|
||||
}
|
||||
result = new String(buffer, packetOffset, len - 1,
|
||||
"ISO-8859-1");
|
||||
packetOffset += len;
|
||||
break;
|
||||
|
||||
// normal ids
|
||||
case HeaderSetImpl.TYPE_LONG:
|
||||
result = new Long(decodeInt());
|
||||
packetOffset += 4;
|
||||
break;
|
||||
|
||||
case HeaderSetImpl.TYPE_UNICODE:
|
||||
len = decodeLength16(packetOffset) - 3;
|
||||
packetOffset += 2;
|
||||
if (len < 2 || buffer[packetOffset + len - 1] != 0
|
||||
|| buffer[packetOffset + len - 2] != 0) {
|
||||
throw new IOException("protocol error, " +
|
||||
"unicode string is not null terminated");
|
||||
}
|
||||
result = new String(buffer,
|
||||
packetOffset, len - 2, "UTF-16BE");
|
||||
// result = new String(buffer, packetOffset, len,
|
||||
// "ISO-8859-1");
|
||||
packetOffset += len;
|
||||
break;
|
||||
|
||||
case HeaderSetImpl.TYPE_BYTEARRAY:
|
||||
len = decodeLength16(packetOffset) - 3;
|
||||
packetOffset += 2;
|
||||
result = new byte[len];
|
||||
System.arraycopy(buffer, packetOffset, result, 0, len);
|
||||
packetOffset += len;
|
||||
break;
|
||||
|
||||
case HeaderSetImpl.TYPE_BYTE:
|
||||
result = new Byte(buffer[packetOffset++]);
|
||||
break;
|
||||
|
||||
case HeaderSetImpl.TYPE_AUTH_CHALLENGE:
|
||||
len = decodeLength16(packetOffset);
|
||||
ObexAuth response =
|
||||
ObexAuth.parseAuthChallenge(buffer, packetOffset-1,
|
||||
len);
|
||||
if (response != null)
|
||||
authResponses.addElement(response);
|
||||
packetOffset += len - 1;
|
||||
return;
|
||||
|
||||
case HeaderSetImpl.TYPE_AUTH_RESPONSE:
|
||||
len = decodeLength16(packetOffset);
|
||||
boolean good =
|
||||
ObexAuth.checkAuthResponse(buffer, packetOffset-1, len,
|
||||
this, authChallenges);
|
||||
if (good) authFailed = false;
|
||||
packetOffset += len - 1;
|
||||
if (DEBUG) {
|
||||
System.out.println("checkAuthResponse() = " + good);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (packetOffset > packetLength) {
|
||||
throw new IOException("protocol error");
|
||||
}
|
||||
|
||||
if (outputType != HeaderSetImpl.TYPE_UNSUPPORTED) {
|
||||
headers.setHeader(headerId, result);
|
||||
} else if (DEBUG) {
|
||||
System.out.println("unsupported header id = 0x"
|
||||
+ Integer.toHexString(headerId).toUpperCase());
|
||||
}
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
throw new IOException("protocol error");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when requested authentication failed.
|
||||
* Implemented on server to call handler.
|
||||
*/
|
||||
abstract void onAuthenticationFailure(byte[] username) throws IOException;
|
||||
|
||||
void onMissingAuthResponse() throws IOException {}
|
||||
|
||||
boolean shouldSendAuthResponse() {
|
||||
return (packetType == ResponseCodes.OBEX_HTTP_UNAUTHORIZED)
|
||||
&& (authResponses.size() != 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parser all packet headers, BODY headers should not apear
|
||||
* and silently ignored.
|
||||
*/
|
||||
final void parsePacketHeaders(HeaderSetImpl headers,
|
||||
int offset) throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.println("parsePacketHeaders()");
|
||||
}
|
||||
|
||||
packetOffset = offset;
|
||||
headers.packetType = buffer[0] & 0xFF;
|
||||
|
||||
parseConnectionID();
|
||||
|
||||
while (packetOffset != packetLength) {
|
||||
parseHeader(headers);
|
||||
}
|
||||
parseEnd();
|
||||
}
|
||||
|
||||
private final void parseConnectionID() {
|
||||
|
||||
int headerId = buffer[packetOffset] & 0xFF;
|
||||
// parse connection ID
|
||||
if (packetOffset + 5 > packetLength
|
||||
|| headerId != HEADER_CONNECTION_ID) {
|
||||
return;
|
||||
}
|
||||
packetOffset++;
|
||||
long id = decodeInt();
|
||||
packetOffset += 4;
|
||||
setConnectionID(id);
|
||||
}
|
||||
|
||||
public abstract void setConnectionID(long id);
|
||||
public abstract long getConnectionID();
|
||||
|
||||
/*
|
||||
* Begin parsing packet headers in packet possibly containing BODY
|
||||
* (data fields).
|
||||
*/
|
||||
final void parsePacketDataBegin(HeaderSetImpl headers, int offset) {
|
||||
if (DEBUG) {
|
||||
System.out.println("parsePacketDataBegin()");
|
||||
}
|
||||
packetOffset = offset;
|
||||
headers.packetType = buffer[0] & 0xFF;
|
||||
|
||||
parseConnectionID();
|
||||
dataOffset = packetOffset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse packet headers, put BODY field content (data) in specified output
|
||||
* array. If output is null, search for a BODY block and return 1 if it is
|
||||
* found.
|
||||
* @return number of bytes put in output.
|
||||
*/
|
||||
final int parsePacketData(HeaderSetImpl headers,
|
||||
byte[] output, int outputOffset, int outputLength)
|
||||
throws IOException {
|
||||
if (DEBUG2) {
|
||||
System.out.println("parsePacketData()");
|
||||
}
|
||||
int result = 0;
|
||||
while (true) {
|
||||
int len = packetOffset - dataOffset;
|
||||
if (DEBUG2) {
|
||||
System.out.print("packetOffset = "+packetOffset+
|
||||
" dataOffset = " +dataOffset);
|
||||
System.out.println(" len = " + len);
|
||||
}
|
||||
if (len > 0) {
|
||||
if (output == null) {
|
||||
// special case for serching first data block
|
||||
// without actual read
|
||||
return 1;
|
||||
}
|
||||
if (outputLength == 0) {
|
||||
return result;
|
||||
}
|
||||
if (len > outputLength) len = outputLength;
|
||||
System.arraycopy(buffer, dataOffset,
|
||||
output, outputOffset, len);
|
||||
outputOffset += len;
|
||||
outputLength -= len;
|
||||
dataOffset += len;
|
||||
result += len;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("packetOffset = " + packetOffset
|
||||
+" packetLength = " + packetLength);
|
||||
}
|
||||
if (packetOffset == packetLength) {
|
||||
return result;
|
||||
}
|
||||
int headerId = buffer[packetOffset] & 0xff;
|
||||
|
||||
if (headerId == HEADER_BODY || headerId == HEADER_EOFBODY) {
|
||||
isEof = (headerId == HEADER_EOFBODY);
|
||||
dataOffset = packetOffset + 3;
|
||||
int length = decodeLength16(packetOffset + 1);
|
||||
if (packetOffset + length > packetLength) {
|
||||
throw new IOException("protocol error");
|
||||
}
|
||||
packetOffset += length;
|
||||
continue;
|
||||
}
|
||||
|
||||
parseHeader(headers);
|
||||
dataOffset = packetOffset;
|
||||
}
|
||||
}
|
||||
|
||||
final void parseEnd() throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.println("parseEnd()");
|
||||
}
|
||||
if (authFailed) {
|
||||
authFailed = false;
|
||||
onMissingAuthResponse();
|
||||
}
|
||||
}
|
||||
|
||||
final int decodeLength16(int off) {
|
||||
return ((((int)buffer[off]) & 0xFF) << 8)
|
||||
+ (((int)buffer[off + 1]) & 0xFF);
|
||||
}
|
||||
|
||||
private final long decodeInt() {
|
||||
return ((buffer[packetOffset+0]& 0xffl) << 24)
|
||||
+ ((buffer[packetOffset+1]& 0xffl) << 16)
|
||||
+ ((buffer[packetOffset+2]& 0xffl) << 8)
|
||||
+ (buffer[packetOffset+3]& 0xffl);
|
||||
}
|
||||
|
||||
private final Calendar decodeTime8601() throws IOException {
|
||||
int year, month, date, hour, minute, second;
|
||||
|
||||
int len = decodeLength16(packetOffset) - 3;
|
||||
packetOffset += 2;
|
||||
|
||||
if (len < 15 || len > 16
|
||||
|| buffer[packetOffset + 8] != 0x54 // 'T'
|
||||
|| (len == 16 && buffer[packetOffset + 15] != 0x5A)) { // 'Z'
|
||||
packetOffset += len;
|
||||
throw new IOException("corrupted time header");
|
||||
}
|
||||
for (int i = 0; i < 14; i++) {
|
||||
if (i == 8) continue;
|
||||
int chr = buffer[packetOffset + i] - 0x30; // '0'
|
||||
if (chr < 0 || chr > 9) {
|
||||
packetOffset += len;
|
||||
throw new IOException("corrupted time header");
|
||||
}
|
||||
}
|
||||
|
||||
year = (buffer[packetOffset+0] - 0x30) * 1000
|
||||
+ (buffer[packetOffset+1] - 0x30) * 100
|
||||
+ (buffer[packetOffset+2] - 0x30) * 10
|
||||
+ (buffer[packetOffset+3] - 0x30);
|
||||
month = (buffer[packetOffset+4] - 0x30) * 10
|
||||
+ (buffer[packetOffset+5] - 0x30);
|
||||
date = (buffer[packetOffset+6] - 0x30) * 10
|
||||
+ (buffer[packetOffset+7] - 0x30);
|
||||
|
||||
hour = (buffer[packetOffset+9] - 0x30) * 10
|
||||
+ (buffer[packetOffset+10] - 0x30);
|
||||
minute = (buffer[packetOffset+11] - 0x30) * 10
|
||||
+ (buffer[packetOffset+12] - 0x30);
|
||||
second = (buffer[packetOffset+13] - 0x30) * 10
|
||||
+ (buffer[packetOffset+14] - 0x30);
|
||||
|
||||
// is check validness of time fields required?
|
||||
|
||||
Calendar cal;
|
||||
// 'len' value 15 means local time,
|
||||
// 16 means UTC (in this case time string has 'Z' suffix)
|
||||
if (len == 16) {
|
||||
cal = Calendar.getInstance(utcTimeZone);
|
||||
} else {
|
||||
cal = Calendar.getInstance();
|
||||
}
|
||||
cal.set(Calendar.YEAR, year);
|
||||
cal.set(Calendar.MONTH, month - 1); // Calendar.JANUARY = 0
|
||||
cal.set(Calendar.DATE, date);
|
||||
|
||||
// ISO 8601 standard uses the 24-hour clock
|
||||
// Therefore you use HOUR_OF_DAY not HOUR
|
||||
cal.set(Calendar.HOUR_OF_DAY, hour);
|
||||
|
||||
cal.set(Calendar.MINUTE, minute);
|
||||
cal.set(Calendar.SECOND, second);
|
||||
|
||||
// set milliseconds to zero since
|
||||
// ISO 8601 uses only second precision
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
packetOffset += len;
|
||||
return cal;
|
||||
}
|
||||
|
||||
static int validateStatus(int status) {
|
||||
switch (status) {
|
||||
case ResponseCodes.OBEX_DATABASE_FULL:
|
||||
case ResponseCodes.OBEX_DATABASE_LOCKED:
|
||||
case ResponseCodes.OBEX_HTTP_ACCEPTED:
|
||||
case ResponseCodes.OBEX_HTTP_BAD_GATEWAY:
|
||||
case ResponseCodes.OBEX_HTTP_BAD_METHOD:
|
||||
case ResponseCodes.OBEX_HTTP_BAD_REQUEST:
|
||||
case ResponseCodes.OBEX_HTTP_CONFLICT:
|
||||
case ResponseCodes.OBEX_HTTP_CREATED:
|
||||
case ResponseCodes.OBEX_HTTP_ENTITY_TOO_LARGE:
|
||||
case ResponseCodes.OBEX_HTTP_FORBIDDEN:
|
||||
case ResponseCodes.OBEX_HTTP_GATEWAY_TIMEOUT:
|
||||
case ResponseCodes.OBEX_HTTP_GONE:
|
||||
case ResponseCodes.OBEX_HTTP_INTERNAL_ERROR:
|
||||
case ResponseCodes.OBEX_HTTP_LENGTH_REQUIRED:
|
||||
case ResponseCodes.OBEX_HTTP_MOVED_PERM:
|
||||
case ResponseCodes.OBEX_HTTP_MOVED_TEMP:
|
||||
case ResponseCodes.OBEX_HTTP_MULT_CHOICE:
|
||||
case ResponseCodes.OBEX_HTTP_NO_CONTENT:
|
||||
case ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE:
|
||||
case ResponseCodes.OBEX_HTTP_NOT_AUTHORITATIVE:
|
||||
case ResponseCodes.OBEX_HTTP_NOT_FOUND:
|
||||
case ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED:
|
||||
case ResponseCodes.OBEX_HTTP_NOT_MODIFIED:
|
||||
case ResponseCodes.OBEX_HTTP_OK:
|
||||
case ResponseCodes.OBEX_HTTP_PARTIAL:
|
||||
case ResponseCodes.OBEX_HTTP_PAYMENT_REQUIRED:
|
||||
case ResponseCodes.OBEX_HTTP_PRECON_FAILED:
|
||||
case ResponseCodes.OBEX_HTTP_PROXY_AUTH:
|
||||
case ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE:
|
||||
case ResponseCodes.OBEX_HTTP_RESET:
|
||||
case ResponseCodes.OBEX_HTTP_SEE_OTHER:
|
||||
case ResponseCodes.OBEX_HTTP_TIMEOUT:
|
||||
case ResponseCodes.OBEX_HTTP_UNAUTHORIZED:
|
||||
case ResponseCodes.OBEX_HTTP_UNAVAILABLE:
|
||||
case ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE:
|
||||
case ResponseCodes.OBEX_HTTP_USE_PROXY:
|
||||
case ResponseCodes.OBEX_HTTP_VERSION:
|
||||
return status;
|
||||
default:
|
||||
return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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.obex;
|
||||
|
||||
import javax.microedition.io.Connection;
|
||||
import java.io.IOException;
|
||||
|
||||
public interface ObexTransport extends Connection {
|
||||
|
||||
/*
|
||||
* Reads the packet data into specified buffer.
|
||||
* <p>
|
||||
* If the specified buffer length is 0, then 0 data
|
||||
* will be read into this buffer, and the rest of packet
|
||||
* data is lost.
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* @param inData the data array to fill with received bytes.
|
||||
*
|
||||
* @exception IOException if a local or remote connection
|
||||
* is closed or I/O error has happen.
|
||||
*
|
||||
* @exception NullPointerException if the specified buffer is null.
|
||||
*/
|
||||
public int read(byte[] inData) throws IOException;
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
* @param outData the buffer with the data to be sent.
|
||||
*
|
||||
* @param len the number of bytes to be sent.
|
||||
*
|
||||
* @exception IOException if a local or remote connection
|
||||
* is closed or I/O error has happen.
|
||||
*
|
||||
* @exception NullPointerException if the specified buffer is null.
|
||||
*/
|
||||
public void write(byte[] outData, int len) throws IOException;
|
||||
|
||||
/*
|
||||
* Determines the amount of data (maximum packet size) that can
|
||||
* be successfully sent in a single write operation. If the size
|
||||
* of data is greater than the maximum packet size, then then only
|
||||
* the first maximum packet size bytes of the packet are sent,
|
||||
* and the rest will be discarded.
|
||||
* <p>
|
||||
* If the returned values is 0, this means the transport
|
||||
* implementation is based on a stream protocol, i.e.
|
||||
* any packet size may be used.
|
||||
*
|
||||
* @return the maximum number of bytes that can be sent/received
|
||||
* in a single call to read()/ write() without losing any data.
|
||||
*/
|
||||
public int getMaximumPacketSize();
|
||||
|
||||
/*
|
||||
* Get underlying connection.
|
||||
*/
|
||||
public Connection getUnderlyingConnection();
|
||||
} // end of interface 'ObexTransport' definition
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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.obex;
|
||||
|
||||
import javax.microedition.io.Connection;
|
||||
import java.io.IOException;
|
||||
|
||||
public interface ObexTransportNotifier extends Connection {
|
||||
|
||||
/*
|
||||
* Accepts transport layer connections.
|
||||
*/
|
||||
public ObexTransport acceptAndOpen() throws IOException;
|
||||
|
||||
public Connection getUnderlyingConnection();
|
||||
} // end of interface 'ObexTransportNotifier' definition
|
|
@ -0,0 +1,342 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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.obex;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Vector;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
public class QueuedHeader {
|
||||
|
||||
/* Debug information, should be false for RR. */
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private ObexPacketStream stream;
|
||||
private int type;
|
||||
private Object value;
|
||||
byte[] buffer;
|
||||
int packetLength;
|
||||
|
||||
QueuedHeader(ObexPacketStream stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* @param type
|
||||
* @param value
|
||||
* @throws IOException
|
||||
*/
|
||||
void sendOrQueue(int type, Object value) throws IOException {
|
||||
if (stream.moreHeaders && send(type, value)) {
|
||||
stream.challengesToSend = true;
|
||||
return;
|
||||
}
|
||||
queue(type, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds the header to queue in ObexPacketStream.
|
||||
* @param type header's type defined in <code>HeaderSetImpl<code> class.
|
||||
* @param value header's value.
|
||||
* @throws IOException thrown if the header is too large.
|
||||
*/
|
||||
void queue(int type, Object value) throws IOException {
|
||||
int maxSendLength = stream.maxSendLength;
|
||||
boolean fail = false;
|
||||
|
||||
switch (HeaderSetImpl.internalType(type)) {
|
||||
// ids which require special handling
|
||||
case HeaderSetImpl.TYPE_SPECIAL_TIME_ISO:
|
||||
case HeaderSetImpl.TYPE_SPECIAL_TIME_4:
|
||||
case HeaderSetImpl.TYPE_LONG:
|
||||
case HeaderSetImpl.TYPE_BYTE:
|
||||
break;
|
||||
|
||||
case HeaderSetImpl.TYPE_SPECIAL_TYPE:
|
||||
// type8 + len16 + head + len16 + bytes[length] + zero8
|
||||
if (((String)value).length() + 7 > maxSendLength) {
|
||||
fail = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case HeaderSetImpl.TYPE_UNICODE:
|
||||
// type8 + len16 + head + len16 + bytes[length] + zero16
|
||||
if ((((String)value).length() << 1) + 8 > maxSendLength) {
|
||||
fail = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case HeaderSetImpl.TYPE_BYTEARRAY:
|
||||
// type8 + len16 + head + len16 + bytes[length]
|
||||
if (((byte[])value).length + 6 > maxSendLength) {
|
||||
fail = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case HeaderSetImpl.TYPE_AUTH_CHALLENGE:
|
||||
Vector challenges = (Vector)value;
|
||||
if (challenges.isEmpty())
|
||||
return;
|
||||
int len = 0;
|
||||
for (int i = 0; i < challenges.size(); i++) {
|
||||
len += ((ObexAuth) challenges.elementAt(i))
|
||||
.prepareChallenge();
|
||||
}
|
||||
if (len + 3 > maxSendLength) {
|
||||
fail = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
stream.headerTooLarge();
|
||||
return;
|
||||
}
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
newHeader();
|
||||
}
|
||||
|
||||
boolean trySendAgain() throws IOException {
|
||||
return send(type, value);
|
||||
}
|
||||
|
||||
boolean send(int type, Object value) throws IOException {
|
||||
buffer = stream.buffer;
|
||||
packetLength = stream.packetLength;
|
||||
int maxSendLength = stream.maxSendLength;
|
||||
if (DEBUG) {
|
||||
System.out.println("Sending header 0x" + Integer.toHexString(type));
|
||||
}
|
||||
|
||||
try {
|
||||
buffer[packetLength++] = (byte)type;
|
||||
switch (HeaderSetImpl.internalType(type)) {
|
||||
// ids which require special handling
|
||||
case HeaderSetImpl.TYPE_SPECIAL_TIME_ISO:
|
||||
encodeTime8601((Calendar)value);
|
||||
break;
|
||||
|
||||
case HeaderSetImpl.TYPE_SPECIAL_TIME_4:
|
||||
encodeInt(((Calendar)value).getTime().getTime() / 1000L);
|
||||
break;
|
||||
|
||||
case HeaderSetImpl.TYPE_SPECIAL_TYPE:
|
||||
byte[] str = ((String)value).getBytes("ISO-8859-1");
|
||||
|
||||
// head + len16 + bytes[length] + zero8
|
||||
encodeLength16(str.length + 4);
|
||||
System.arraycopy(str, 0, buffer, packetLength,
|
||||
str.length);
|
||||
packetLength += str.length;
|
||||
// null terminator required by protocol spec
|
||||
buffer[packetLength++] = 0;
|
||||
break;
|
||||
|
||||
case HeaderSetImpl.TYPE_AUTH_CHALLENGE:
|
||||
packetLength--;
|
||||
Vector challenges = (Vector) value;
|
||||
|
||||
stream.authFailed = false;
|
||||
|
||||
for (int i = 0; i < challenges.size(); i++) {
|
||||
ObexAuth auth = (ObexAuth) challenges.elementAt(i);
|
||||
packetLength += auth.addChallenge(buffer, packetLength);
|
||||
}
|
||||
|
||||
// need to be shure - challenges are added
|
||||
if (packetLength > maxSendLength) {
|
||||
break;
|
||||
}
|
||||
|
||||
// if we still there, then authChallenges
|
||||
// successfully added
|
||||
for (int i = 0; i < challenges.size(); i++) {
|
||||
stream.authFailed = true;
|
||||
stream.authChallenges.addElement(
|
||||
challenges.elementAt(i));
|
||||
}
|
||||
break;
|
||||
|
||||
// normal ids
|
||||
|
||||
// 4 byte integer values, range (0xC0 - 0xFF)
|
||||
// system and user defined
|
||||
case HeaderSetImpl.TYPE_LONG:
|
||||
encodeInt(((Long)value).longValue());
|
||||
break;
|
||||
|
||||
// unicode encoded string values, range (0x00 - 0x3F)
|
||||
// system and user defined
|
||||
case HeaderSetImpl.TYPE_UNICODE:
|
||||
str = ((String)value).getBytes("UTF-16BE");
|
||||
// str = ((String)value).getBytes("ISO-8859-1");
|
||||
|
||||
// head + len16 + bytes[length] + zero16
|
||||
encodeLength16(str.length + 5);
|
||||
System.arraycopy(str, 0, buffer, packetLength,
|
||||
str.length);
|
||||
packetLength += str.length;
|
||||
// end of unicode string
|
||||
buffer[packetLength++] = 0;
|
||||
buffer[packetLength++] = 0;
|
||||
break;
|
||||
|
||||
// byte[] array values, range (0x40 - 0x7F)
|
||||
// system and user defined
|
||||
case HeaderSetImpl.TYPE_BYTEARRAY:
|
||||
byte[] array = (byte[]) value;
|
||||
|
||||
// head + len16 + bytes[length]
|
||||
encodeLength16(array.length + 3);
|
||||
System.arraycopy(array, 0, buffer, packetLength,
|
||||
array.length);
|
||||
packetLength += array.length;
|
||||
stream.containsTargetHeader |=
|
||||
(type == HeaderSetImpl.TARGET);
|
||||
break;
|
||||
|
||||
// byte values, range (0x80 - 0xBF)
|
||||
// user defined
|
||||
case HeaderSetImpl.TYPE_BYTE:
|
||||
buffer[packetLength++] =
|
||||
(byte)((Byte)value).byteValue();
|
||||
break;
|
||||
|
||||
// case HeaderSetImpl.TYPE_UNSUPPORTED:
|
||||
default:
|
||||
if (DEBUG) {
|
||||
System.out.println("wrong or unsupported header");
|
||||
}
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e.toString());
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
stream.moreHeaders = false;
|
||||
return false;
|
||||
}
|
||||
if (packetLength > maxSendLength) {
|
||||
stream.moreHeaders = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// finally closing body headers if opened
|
||||
if (stream.dataOpened) {
|
||||
int dataOffset = stream.dataOffset;
|
||||
int len = stream.packetLength - dataOffset;
|
||||
buffer[dataOffset+1] = (byte)(len >> 8);
|
||||
buffer[dataOffset+2] = (byte)len;
|
||||
stream.dataOpened = false;
|
||||
stream.dataClosed = true;
|
||||
}
|
||||
stream.packetLength = packetLength;
|
||||
|
||||
// gc header value
|
||||
this.value = null;
|
||||
if (DEBUG) {
|
||||
System.out.println(" -> sent!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void newHeader() {
|
||||
stream.queuedHeaders.addElement(this);
|
||||
if (stream.emptyHeadersPool.empty()) {
|
||||
stream.newHeader = new QueuedHeader(stream);
|
||||
} else {
|
||||
stream.newHeader = (QueuedHeader) stream.emptyHeadersPool.pop();
|
||||
}
|
||||
}
|
||||
|
||||
boolean sendAllQueued() throws IOException {
|
||||
while (stream.queuedHeaders.size() > 0 && stream.moreHeaders) {
|
||||
QueuedHeader header = (QueuedHeader)
|
||||
stream.queuedHeaders.firstElement();
|
||||
boolean res = header.trySendAgain();
|
||||
if (!res) {
|
||||
return false;
|
||||
}
|
||||
stream.queuedHeaders.removeElementAt(0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private final void encodeInt(long val) {
|
||||
buffer[packetLength++] = (byte)(val >> 24);
|
||||
buffer[packetLength++] = (byte)(val >> 16);
|
||||
buffer[packetLength++] = (byte)(val >> 8);
|
||||
buffer[packetLength++] = (byte)val;
|
||||
}
|
||||
|
||||
private final void encodeLength16(int value) {
|
||||
buffer[packetLength++] = (byte)(value >> 8);
|
||||
buffer[packetLength++] = (byte)value;
|
||||
}
|
||||
|
||||
private final void encodeTime8601(Calendar cal) {
|
||||
encodeLength16(19);
|
||||
|
||||
// copy calendar as it can be with wrong timezone.
|
||||
Calendar cal2 = Calendar.getInstance(ObexPacketStream.utcTimeZone);
|
||||
cal2.setTime(cal.getTime());
|
||||
int year = cal2.get(Calendar.YEAR);
|
||||
int month = cal2.get(Calendar.MONTH) + 1; // Calendar.JANUARY = 0
|
||||
int date = cal2.get(Calendar.DATE);
|
||||
int hour = cal2.get(Calendar.HOUR_OF_DAY);
|
||||
int minute = cal2.get(Calendar.MINUTE);
|
||||
int second = cal2.get(Calendar.SECOND);
|
||||
int zero = 0x30; // zero ('0') code in latin1
|
||||
|
||||
if (year < 0 || year > 9999) {
|
||||
if (DEBUG) {
|
||||
System.out.println("wrong date header");
|
||||
}
|
||||
}
|
||||
buffer[packetLength++] = (byte)(year / 1000 + zero);
|
||||
year = year % 1000;
|
||||
buffer[packetLength++] = (byte)(year / 100 + zero);
|
||||
year = year % 100;
|
||||
buffer[packetLength++] = (byte)(year / 10 + zero);
|
||||
year = year % 10;
|
||||
buffer[packetLength++] = (byte)(year + zero);
|
||||
buffer[packetLength++] = (byte)(month / 10 + zero);
|
||||
buffer[packetLength++] = (byte)(month % 10 + zero);
|
||||
buffer[packetLength++] = (byte)(date / 10 + zero);
|
||||
buffer[packetLength++] = (byte)(date % 10 + zero);
|
||||
buffer[packetLength++] = 0x54; // 'T' code in latin1
|
||||
buffer[packetLength++] = (byte)(hour / 10 + zero);
|
||||
buffer[packetLength++] = (byte)(hour % 10 + zero);
|
||||
buffer[packetLength++] = (byte)(minute / 10 + zero);
|
||||
buffer[packetLength++] = (byte)(minute % 10 + zero);
|
||||
buffer[packetLength++] = (byte)(second / 10 + zero);
|
||||
buffer[packetLength++] = (byte)(second % 10 + zero);
|
||||
buffer[packetLength++] = (byte)0x5A; // 'Z' code in latin1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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.obex;
|
||||
|
||||
// J2ME security classes
|
||||
import com.sun.j2me.crypto.MessageDigest;
|
||||
import com.sun.j2me.crypto.NoSuchAlgorithmException;
|
||||
import com.sun.j2me.crypto.DigestException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/*
|
||||
* The platform dependent class which provides a wrapper for
|
||||
* security API in J2ME or J2SE.
|
||||
*/
|
||||
final class SSLWrapper {
|
||||
private MessageDigest md5;
|
||||
|
||||
SSLWrapper() throws IOException {
|
||||
try {
|
||||
md5 = new MessageDigest("MD5");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
void update(byte[] input, int offset, int length) {
|
||||
md5.update(input, offset, length);
|
||||
}
|
||||
|
||||
void doFinal(byte[] srcData, int srcOff, int srcLen, byte[] dstData,
|
||||
int dstOff) {
|
||||
|
||||
if (srcLen != 0) {
|
||||
md5.update(srcData, srcOff, srcLen);
|
||||
}
|
||||
|
||||
try {
|
||||
md5.digest(dstData, dstOff, dstData.length - dstOff);
|
||||
} catch (DigestException e) {
|
||||
// Buffer too short
|
||||
throw new RuntimeException("output buffer too short");
|
||||
}
|
||||
}
|
||||
} // end of class 'SSLWrapper' definition
|
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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.obex;
|
||||
|
||||
import javax.obex.Authenticator;
|
||||
import javax.obex.ServerRequestHandler;
|
||||
import javax.obex.ResponseCodes;
|
||||
import javax.obex.HeaderSet;
|
||||
import java.io.IOException;
|
||||
import javax.microedition.io.Connection;
|
||||
|
||||
class ServerConnectionImpl extends ObexPacketStream
|
||||
implements Connection, Runnable {
|
||||
|
||||
/* Debug information, should be false for RR. */
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
ServerRequestHandler handler;
|
||||
|
||||
private int owner;
|
||||
private boolean isConnected;
|
||||
|
||||
private long connId;
|
||||
|
||||
/* Current operation header size overflow flag. */
|
||||
boolean operationHeadersOverflow = false;
|
||||
|
||||
/* Current operation state. */
|
||||
boolean operationClosed;
|
||||
|
||||
void headerTooLarge() throws IOException {
|
||||
operationHeadersOverflow = true;
|
||||
operationClosed = true;
|
||||
}
|
||||
|
||||
ServerConnectionImpl(ObexTransport transport,
|
||||
ServerRequestHandler handler, Authenticator auth)
|
||||
throws IOException {
|
||||
super(transport);
|
||||
this.handler = handler;
|
||||
isClient = false;
|
||||
authenticator = auth;
|
||||
|
||||
// owner field of all created HeaderSets
|
||||
owner = HeaderSetImpl.OWNER_SERVER;
|
||||
new Thread(this).start();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
while (processRequest()) {
|
||||
// if connection closed
|
||||
if (isClosed()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
if (DEBUG) {
|
||||
System.out.println("ServerConnectionImpl thread exception");
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (DEBUG) {
|
||||
System.out.println("ServerConnectionImpl: client disconnected");
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
/*
|
||||
* Modified sendPacket() function.
|
||||
* If packet is too large - sends OBEX_HTTP_REQ_TOO_LARGE response.
|
||||
*/
|
||||
private int sendResponsePacket(byte[] head,
|
||||
HeaderSetImpl headers) throws IOException {
|
||||
int status = head[0] & 0xFF;
|
||||
packetBegin(head);
|
||||
packetAddConnectionID(getConnectionID(), headers);
|
||||
packetAddAuthResponses();
|
||||
packetAddHeaders(headers);
|
||||
if (!queuedHeaders.isEmpty() || operationHeadersOverflow) {
|
||||
queuedHeaders.removeAllElements();
|
||||
setPacketType(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE);
|
||||
}
|
||||
packetEnd();
|
||||
return status;
|
||||
}
|
||||
|
||||
private void doConnect() throws IOException {
|
||||
// NOTE client may not authenticated the server and wants to
|
||||
// so allow multiple connect requests
|
||||
HeaderSetImpl inputHeaderSet = new HeaderSetImpl(owner);
|
||||
HeaderSetImpl responseHeaderSet = new HeaderSetImpl(owner);
|
||||
|
||||
// server side check
|
||||
if (buffer[3] != 0x10 || packetLength < 7) {
|
||||
// IMPL_NOTE: It is not decided what to do if the OBEX version number
|
||||
// is different from the one we support (which is presumably 1.0).
|
||||
// Windows uses version 1.2, Linux uses version 1.1, and we
|
||||
// probably want to work with both.
|
||||
// throw new IOException("unsupported client obex version");
|
||||
}
|
||||
// ignore flags
|
||||
// save maximum client supported packet size
|
||||
maxSendLength = decodeLength16(5);
|
||||
|
||||
if (maxSendLength > OBEX_MAXIMUM_PACKET_LENGTH) {
|
||||
maxSendLength = OBEX_MAXIMUM_PACKET_LENGTH;
|
||||
}
|
||||
parsePacketHeaders(inputHeaderSet, 7);
|
||||
// processMissingAuthentications();
|
||||
|
||||
int status = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
|
||||
|
||||
try {
|
||||
status = handler.onConnect(inputHeaderSet,
|
||||
responseHeaderSet);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
status = validateStatus(status);
|
||||
|
||||
if (status != ResponseCodes.OBEX_HTTP_OK) {
|
||||
// lets client will authenticate first
|
||||
authResponses.removeAllElements();
|
||||
}
|
||||
|
||||
byte[] head = new byte[] {
|
||||
(byte)status,
|
||||
0, 0, // length will be here
|
||||
0x10, // version 1.0 of OBEX
|
||||
0x0, // flags
|
||||
(byte) (OBEX_MAXIMUM_PACKET_LENGTH / 0x100), // maximum client
|
||||
(byte) (OBEX_MAXIMUM_PACKET_LENGTH % 0x100), // supported packet
|
||||
// length
|
||||
};
|
||||
|
||||
status = sendResponsePacket(head, responseHeaderSet);
|
||||
|
||||
if (status == ResponseCodes.OBEX_HTTP_OK) {
|
||||
isConnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean notConnected() throws IOException {
|
||||
if (!isConnected) {
|
||||
HeaderSetImpl headers = new HeaderSetImpl(owner);
|
||||
headers.setHeader(HeaderSet.DESCRIPTION, "not connected");
|
||||
sendPacket(PACKET_BAD_REQUEST, getConnectionID(), headers, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void onAuthenticationFailure(byte[] username) throws IOException {
|
||||
try {
|
||||
if (DEBUG) {
|
||||
System.out.println("ServerConnectionImpl:"
|
||||
+ " handler.onAuthenticationFailure()");
|
||||
}
|
||||
handler.onAuthenticationFailure(username);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
operationClosed = true;
|
||||
}
|
||||
|
||||
private void doDisconnect() throws IOException {
|
||||
if (notConnected()) {
|
||||
return;
|
||||
}
|
||||
HeaderSetImpl inputHeaderSet = new HeaderSetImpl(owner);
|
||||
HeaderSetImpl responseHeaderSet = new HeaderSetImpl(owner);
|
||||
|
||||
parsePacketHeaders(inputHeaderSet, 3);
|
||||
// processMissingAuthentications();
|
||||
|
||||
try {
|
||||
handler.onDisconnect(inputHeaderSet, responseHeaderSet);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
byte[] head = new byte[] {
|
||||
(byte) ResponseCodes.OBEX_HTTP_OK,
|
||||
0, 0, // length will be here
|
||||
};
|
||||
|
||||
int status = sendResponsePacket(head, responseHeaderSet);
|
||||
|
||||
if (status == ResponseCodes.OBEX_HTTP_OK) {
|
||||
isConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void doPut(HeaderSetImpl inputHeaderSet) throws IOException {
|
||||
int status = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
|
||||
ServerOperation op =
|
||||
new ServerOperation(this, inputHeaderSet);
|
||||
try {
|
||||
status = handler.onPut(op);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
status = validateStatus(status);
|
||||
if (operationHeadersOverflow) {
|
||||
status = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
|
||||
}
|
||||
op.destroy(status);
|
||||
}
|
||||
|
||||
private void doDelete(HeaderSetImpl inputHeaderSet) throws IOException {
|
||||
HeaderSetImpl responseHeaderSet = new HeaderSetImpl(owner);
|
||||
int status = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
|
||||
try {
|
||||
status = handler.onDelete(inputHeaderSet, responseHeaderSet);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
status = validateStatus(status);
|
||||
byte[] head = new byte[] {
|
||||
(byte)status,
|
||||
0, 0, // length will be here
|
||||
};
|
||||
|
||||
sendResponsePacket(head, responseHeaderSet);
|
||||
}
|
||||
|
||||
private void doPutOrDelete() throws IOException {
|
||||
if (notConnected()) {
|
||||
return;
|
||||
}
|
||||
HeaderSetImpl inputHeaderSet = new HeaderSetImpl(owner);
|
||||
|
||||
int mode = ServerOperation.waitForData(this,
|
||||
inputHeaderSet, OPCODE_PUT);
|
||||
|
||||
switch (mode) {
|
||||
case 0: sendPacket(PACKET_SUCCESS, getConnectionID(), null, true);
|
||||
return;
|
||||
case 1: doPut(inputHeaderSet); return;
|
||||
case 2: doDelete(inputHeaderSet); return;
|
||||
default:return;
|
||||
}
|
||||
}
|
||||
|
||||
private void doGet() throws IOException {
|
||||
if (notConnected()) {
|
||||
return;
|
||||
}
|
||||
int status = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
|
||||
ServerOperation op =
|
||||
new ServerOperation(this);
|
||||
try {
|
||||
status = handler.onGet(op);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
status = validateStatus(status);
|
||||
if (operationHeadersOverflow) {
|
||||
status = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
|
||||
}
|
||||
op.destroy(status);
|
||||
}
|
||||
|
||||
private void doSetPath() throws IOException {
|
||||
if (notConnected()) {
|
||||
return;
|
||||
}
|
||||
HeaderSetImpl inputHeaderSet = new HeaderSetImpl(owner);
|
||||
HeaderSetImpl responseHeaderSet = new HeaderSetImpl(owner);
|
||||
|
||||
// check flags
|
||||
boolean create = ((buffer[3] & 2) == 0);
|
||||
boolean backup = ((buffer[3] & 1) == 1);
|
||||
|
||||
parsePacketHeaders(inputHeaderSet, 5);
|
||||
// processMissingAuthentications();
|
||||
int status = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
|
||||
try {
|
||||
status = handler.onSetPath(inputHeaderSet,
|
||||
responseHeaderSet, backup, create);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
status = validateStatus(status);
|
||||
byte[] head = new byte[] {
|
||||
(byte)status,
|
||||
0, 0, // length will be here
|
||||
};
|
||||
sendResponsePacket(head, responseHeaderSet);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process one client request
|
||||
* @return false when connection closed
|
||||
*/
|
||||
private boolean processRequest() throws IOException {
|
||||
try {
|
||||
recvPacket();
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
HeaderSetImpl inputHeaderSet = new HeaderSetImpl(owner);
|
||||
operationHeadersOverflow = false;
|
||||
operationClosed = false;
|
||||
isEof = false;
|
||||
|
||||
switch (packetType) {
|
||||
case OPCODE_CONNECT:
|
||||
doConnect();
|
||||
break;
|
||||
|
||||
case OPCODE_DISCONNECT:
|
||||
doDisconnect();
|
||||
break;
|
||||
|
||||
case OPCODE_PUT:
|
||||
case OPCODE_PUT | OPCODE_FINAL:
|
||||
doPutOrDelete();
|
||||
break;
|
||||
|
||||
case OPCODE_GET:
|
||||
case OPCODE_GET | OPCODE_FINAL:
|
||||
doGet();
|
||||
break;
|
||||
case OPCODE_SETPATH:
|
||||
doSetPath();
|
||||
break;
|
||||
|
||||
case OPCODE_ABORT:
|
||||
// ignore abort, it is too late, any of the operations is
|
||||
// finished
|
||||
byte[] head = new byte[] {
|
||||
(byte) ResponseCodes.OBEX_HTTP_OK,
|
||||
0, 0, // length will be here
|
||||
};
|
||||
sendResponsePacket(head, null);
|
||||
break;
|
||||
|
||||
default:
|
||||
// wrong packet received, ignoring
|
||||
if (DEBUG) {
|
||||
System.out.println("Wrong packet: id = "
|
||||
+ inputHeaderSet.packetType + " length = "
|
||||
+ packetLength);
|
||||
}
|
||||
sendPacket(PACKET_NOT_IMPLEMENTED, getConnectionID(),
|
||||
null, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setConnectionID(long id) {
|
||||
try { // may by overloaded by user and throw exception
|
||||
connId = id;
|
||||
handler.setConnectionID(id);
|
||||
} catch (Throwable e) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
|
||||
public long getConnectionID() {
|
||||
try { // may by overloaded by user and throw exception
|
||||
long id = handler.getConnectionID();
|
||||
if (connId == id) {
|
||||
return -1;
|
||||
}
|
||||
connId = id;
|
||||
return id;
|
||||
} catch (Throwable e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,530 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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.obex;
|
||||
|
||||
import javax.obex.Operation;
|
||||
import javax.obex.HeaderSet;
|
||||
import javax.obex.ResponseCodes;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
|
||||
/*
|
||||
* The class implements server side of put/get operation.
|
||||
*/
|
||||
final class ServerOperation implements Operation {
|
||||
// Debug information, should be false for RR
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private Object lock = new Object();
|
||||
private HeaderSetImpl recvHeaders;
|
||||
private ServerConnectionImpl stream;
|
||||
private int opcode;
|
||||
private boolean isGet;
|
||||
|
||||
private boolean isAborted;
|
||||
private boolean requestEnd;
|
||||
|
||||
private boolean inputStreamOpened = false;
|
||||
private boolean outputStreamOpened = false;
|
||||
private boolean inputStreamEof;
|
||||
private boolean responseIsSent = false;
|
||||
|
||||
private OperationInputStream is = new OperationInputStream();
|
||||
private OperationOutputStream os = new OperationOutputStream();
|
||||
|
||||
private byte[] head = ObexPacketStream.PACKET_CONTINUE;
|
||||
|
||||
|
||||
/* Constructor for get operation. */
|
||||
ServerOperation(ServerConnectionImpl stream) throws IOException {
|
||||
this.stream = stream;
|
||||
opcode = ObexPacketStream.OPCODE_GET;
|
||||
isGet = true;
|
||||
recvHeaders = new HeaderSetImpl(HeaderSetImpl.OWNER_SERVER);
|
||||
int mode = waitForData(stream,
|
||||
recvHeaders, ObexPacketStream.OPCODE_GET);
|
||||
switch (mode) {
|
||||
case 0:
|
||||
isAborted = true;
|
||||
stream.operationClosed = true;
|
||||
break;
|
||||
case 2:
|
||||
requestEnd = true;
|
||||
// no data was received
|
||||
inputStreamEof = true;
|
||||
stream.packetBegin(head);
|
||||
// Response packets can contains both TARGET and CONN ID headers
|
||||
stream.packetAddConnectionID(stream.getConnectionID(), null);
|
||||
stream.packetAddAuthResponses();
|
||||
stream.packetAddHeaders(null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Constructor for put operation. */
|
||||
ServerOperation(ServerConnectionImpl stream, HeaderSetImpl recvHeaders) {
|
||||
// data parsing mode already on.
|
||||
this.stream = stream;
|
||||
opcode = ObexPacketStream.OPCODE_PUT;
|
||||
isGet = false;
|
||||
this.recvHeaders = recvHeaders;
|
||||
|
||||
}
|
||||
|
||||
public DataOutputStream openDataOutputStream() throws IOException {
|
||||
return new DataOutputStream(openOutputStream());
|
||||
}
|
||||
|
||||
public OutputStream openOutputStream() throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.println("server: openOutputStream()");
|
||||
}
|
||||
synchronized (lock) {
|
||||
if (stream.operationClosed) {
|
||||
throw new IOException("operation closed");
|
||||
}
|
||||
if (outputStreamOpened) {
|
||||
throw new IOException("no more output streams available");
|
||||
}
|
||||
if (!requestEnd) {
|
||||
throw new IOException("input data not read out");
|
||||
}
|
||||
outputStreamOpened = true;
|
||||
return os;
|
||||
}
|
||||
}
|
||||
|
||||
public DataInputStream openDataInputStream() throws IOException {
|
||||
return new DataInputStream(openInputStream());
|
||||
}
|
||||
|
||||
public InputStream openInputStream() throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.println("server: openInputStream()");
|
||||
}
|
||||
synchronized (lock) {
|
||||
if (stream.operationClosed) {
|
||||
throw new IOException("operation closed");
|
||||
}
|
||||
if (inputStreamOpened) {
|
||||
throw new IOException("no more input streams available");
|
||||
}
|
||||
inputStreamOpened = true;
|
||||
return is;
|
||||
}
|
||||
}
|
||||
|
||||
public void abort() throws IOException {
|
||||
// forbidden on server
|
||||
throw new IOException("not permitted");
|
||||
}
|
||||
|
||||
public HeaderSet getReceivedHeaders() throws IOException {
|
||||
synchronized (lock) {
|
||||
if (stream.operationClosed) {
|
||||
throw new IOException("operation closed");
|
||||
}
|
||||
return new HeaderSetImpl(recvHeaders);
|
||||
}
|
||||
}
|
||||
|
||||
public int getResponseCode() throws IOException {
|
||||
// forbidden on server
|
||||
throw new IOException("not permitted");
|
||||
}
|
||||
|
||||
public String getEncoding() {
|
||||
return null; // acording to docs
|
||||
}
|
||||
|
||||
public long getLength() {
|
||||
Long res = (Long)recvHeaders.getHeader(HeaderSetImpl.LENGTH);
|
||||
if (res == null) {
|
||||
return -1;
|
||||
}
|
||||
return res.longValue();
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return (String)recvHeaders.getHeader(HeaderSetImpl.TYPE);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
stream.operationClosed = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by ServerRequestHandler to finish any activity remaining
|
||||
* and to return errorcode to client.
|
||||
*/
|
||||
void destroy(int status) {
|
||||
if (DEBUG) {
|
||||
System.out.println("server: destroy()");
|
||||
}
|
||||
try {
|
||||
outputStreamOpened = false;
|
||||
if (!responseIsSent) {
|
||||
if (!isGet) {
|
||||
stream.packetBegin(head);
|
||||
stream.packetAddConnectionID(stream.getConnectionID(), null);
|
||||
stream.packetAddAuthResponses();
|
||||
}
|
||||
stream.packetAddHeaders(null);
|
||||
close();
|
||||
if (isAborted) status = ResponseCodes.OBEX_HTTP_OK;
|
||||
stream.setPacketType(status);
|
||||
stream.packetEnd(); // send final packet
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// ignore
|
||||
}
|
||||
// remaining headers will be lost
|
||||
stream.queuedHeaders.removeAllElements();
|
||||
}
|
||||
|
||||
public void sendHeaders(HeaderSet headers) throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.println("server: sendHeaders()");
|
||||
}
|
||||
synchronized (lock) {
|
||||
if (stream.operationClosed) {
|
||||
throw new IOException("operation closed");
|
||||
}
|
||||
if (headers == null) {
|
||||
throw new NullPointerException("null headerset");
|
||||
}
|
||||
if (!(headers instanceof HeaderSetImpl)) {
|
||||
throw new IllegalArgumentException("wrong headerset class");
|
||||
}
|
||||
HeaderSetImpl headersImpl = (HeaderSetImpl) headers;
|
||||
if (!headersImpl.isSendable()) {
|
||||
throw new IllegalArgumentException(
|
||||
"not created with createHeaderSet");
|
||||
}
|
||||
|
||||
stream.packetAddHeaders(headersImpl);
|
||||
|
||||
if (requestEnd && isGet) {
|
||||
while (!stream.queuedHeaders.isEmpty()) {
|
||||
packetExchange();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void packetExchange() throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.println("server: packetExchange()");
|
||||
}
|
||||
if (stream.operationHeadersOverflow) {
|
||||
throw new IOException("operation terminated, too long headers");
|
||||
}
|
||||
if (!requestEnd) {
|
||||
// reading out input stream
|
||||
requestEnd =
|
||||
stream.packetType == (opcode | ObexPacketStream.OPCODE_FINAL);
|
||||
|
||||
// inordenary case: EOF-DATA but no final bit
|
||||
if (stream.isEof && !requestEnd) {
|
||||
while (recvHeaders.packetType
|
||||
== ObexPacketStream.OPCODE_PUT) {
|
||||
// not final - waiting for final, data not allowed
|
||||
// after EOFB
|
||||
stream.sendPacket(head, stream.getConnectionID(),
|
||||
null, false);
|
||||
stream.recvPacket();
|
||||
stream.parsePacketHeaders(recvHeaders, 3);
|
||||
}
|
||||
|
||||
if (recvHeaders.packetType
|
||||
== ObexPacketStream.OPCODE_ABORT) {
|
||||
stream.operationClosed = true;
|
||||
isAborted = true;
|
||||
throw new IOException("operation aborted");
|
||||
}
|
||||
|
||||
if (stream.packetType !=
|
||||
(opcode | ObexPacketStream.OPCODE_FINAL)) {
|
||||
stream.operationClosed = true;
|
||||
stream.brokenLink();
|
||||
throw new IOException("protocol error");
|
||||
}
|
||||
}
|
||||
if (requestEnd) {
|
||||
// switch to requestEnd packetExchange mode
|
||||
stream.packetBegin(head);
|
||||
stream.packetAddConnectionID(stream.getConnectionID(), null);
|
||||
stream.packetAddAuthResponses();
|
||||
stream.packetAddHeaders(null);
|
||||
return;
|
||||
}
|
||||
// stream.parseEnd();
|
||||
stream.sendPacket(ObexPacketStream.PACKET_CONTINUE,
|
||||
stream.getConnectionID(), null, false);
|
||||
stream.recvPacket();
|
||||
|
||||
if (stream.packetType == ObexPacketStream.OPCODE_ABORT) {
|
||||
stream.parsePacketHeaders(recvHeaders, 3);
|
||||
isAborted = true;
|
||||
stream.operationClosed = true;
|
||||
throw new IOException("operation aborted");
|
||||
}
|
||||
|
||||
if ((stream.packetType & ~ObexPacketStream.OPCODE_FINAL)
|
||||
!= opcode) {
|
||||
stream.operationClosed = true;
|
||||
stream.brokenLink();
|
||||
throw new IOException("protocol error");
|
||||
}
|
||||
stream.parsePacketDataBegin(recvHeaders, 3);
|
||||
return;
|
||||
}
|
||||
stream.packetEnd();
|
||||
stream.recvPacket();
|
||||
stream.parsePacketHeaders(recvHeaders, 3);
|
||||
|
||||
if (stream.packetType == ObexPacketStream.OPCODE_ABORT) {
|
||||
// prepare response packet
|
||||
stream.packetBegin(ObexPacketStream.PACKET_SUCCESS);
|
||||
stream.packetAddConnectionID(stream.getConnectionID(), null);
|
||||
stream.packetAddAuthResponses();
|
||||
stream.packetAddHeaders(null);
|
||||
|
||||
isAborted = true;
|
||||
stream.operationClosed = true;
|
||||
throw new IOException("operation aborted");
|
||||
}
|
||||
|
||||
if (stream.packetType == ObexPacketStream.OPCODE_DISCONNECT) {
|
||||
stream.sendPacket(ObexPacketStream.PACKET_SUCCESS,
|
||||
stream.getConnectionID(), null, false);
|
||||
|
||||
stream.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (stream.packetType != (opcode | ObexPacketStream.OPCODE_FINAL)) {
|
||||
stream.operationClosed = true;
|
||||
stream.brokenLink();
|
||||
throw new IOException("protocol error");
|
||||
}
|
||||
|
||||
stream.packetBegin(head);
|
||||
stream.packetAddConnectionID(stream.getConnectionID(), null);
|
||||
stream.packetAddAuthResponses();
|
||||
stream.packetAddHeaders(null);
|
||||
}
|
||||
|
||||
static int waitForData(ServerConnectionImpl stream,
|
||||
HeaderSetImpl inputHeaderSet, int op) throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.println("server: waitForData()");
|
||||
}
|
||||
|
||||
// check of errorcode should be done before after data parsing
|
||||
stream.parsePacketDataBegin(inputHeaderSet, 3);
|
||||
|
||||
// special request to check data availability
|
||||
int hasData = stream.parsePacketData(inputHeaderSet, null, 0, 0);
|
||||
|
||||
// waiting for data or final bit or abort
|
||||
while (true) {
|
||||
if (stream.packetType == ObexPacketStream.OPCODE_ABORT) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hasData == 1 || stream.isEof) {
|
||||
return 1; // has data
|
||||
}
|
||||
|
||||
if (stream.packetType == (op | ObexPacketStream.OPCODE_FINAL)) {
|
||||
return 2; // final
|
||||
}
|
||||
|
||||
if (stream.packetType != op) {
|
||||
stream.brokenLink();
|
||||
throw new IOException("protocol error");
|
||||
}
|
||||
|
||||
stream.sendPacket(ObexPacketStream.PACKET_CONTINUE,
|
||||
stream.getConnectionID(), null, false);
|
||||
stream.recvPacket();
|
||||
|
||||
// check of errorcode should be done before after data parsing
|
||||
stream.parsePacketDataBegin(inputHeaderSet, 3);
|
||||
|
||||
// special request to check data availability
|
||||
hasData = stream.parsePacketData(inputHeaderSet, null, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private class OperationInputStream extends InputStream {
|
||||
OperationInputStream() {}
|
||||
|
||||
public int read() throws IOException {
|
||||
byte[] b = new byte[1];
|
||||
int len = read(b, 0, 1);
|
||||
if (len == -1) {
|
||||
return -1;
|
||||
}
|
||||
return b[0] & 0xFF;
|
||||
}
|
||||
|
||||
public int read(byte[] b, int offset, int len) throws IOException {
|
||||
if (DEBUG) {
|
||||
// System.out.println("server: is.read()");
|
||||
}
|
||||
synchronized (lock) {
|
||||
if (!inputStreamOpened) {
|
||||
throw new IOException("operation finished");
|
||||
}
|
||||
// Nullpointer check is here
|
||||
if (len < 0 || offset < 0 || offset + len > b.length) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (inputStreamEof) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
while (true) {
|
||||
int rd = stream.parsePacketData(recvHeaders, b,
|
||||
offset, len);
|
||||
if (rd != 0) {
|
||||
offset += rd;
|
||||
len -= rd;
|
||||
result += rd;
|
||||
if (len == 0) {
|
||||
if (stream.dataOffset != stream.packetOffset) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((len == 0) && !stream.isEof) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// need more data, packet is finished
|
||||
if (stream.isEof) {
|
||||
inputStreamEof = true;
|
||||
if (stream.dataOffset == stream.packetOffset) {
|
||||
requestEnd = stream.packetType ==
|
||||
(opcode | ObexPacketStream.OPCODE_FINAL);
|
||||
return (result == 0) ? -1 : result;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
packetExchange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.println("server: is.close()");
|
||||
}
|
||||
// errorcode unknown yet,
|
||||
// ServerRequestHandler will send errorcode packet
|
||||
synchronized (lock) {
|
||||
inputStreamOpened = false;
|
||||
inputStreamEof = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class OperationOutputStream extends OutputStream {
|
||||
OperationOutputStream() {}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
write(new byte[] { (byte)b }, 0, 1);
|
||||
}
|
||||
|
||||
public void write(byte[] b, int offset, int len) throws IOException {
|
||||
if (DEBUG) {
|
||||
// System.out.println("server: os.write()");
|
||||
}
|
||||
synchronized (lock) {
|
||||
if (!outputStreamOpened) {
|
||||
throw new IOException("operation finished");
|
||||
}
|
||||
if (len < 0 || offset < 0 || offset + len > b.length) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
while (len > 0) {
|
||||
int wr = stream.packetAddData(b, offset, len);
|
||||
if (wr != len) {
|
||||
packetExchange();
|
||||
}
|
||||
len -= wr;
|
||||
offset += wr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.println("server: os.flush()");
|
||||
}
|
||||
synchronized (lock) {
|
||||
if (!outputStreamOpened) {
|
||||
throw new IOException("operation finished");
|
||||
}
|
||||
if (isGet) {
|
||||
packetExchange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void close() throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.println("server: os.close()");
|
||||
}
|
||||
|
||||
synchronized (lock) {
|
||||
if (outputStreamOpened) {
|
||||
outputStreamOpened = false;
|
||||
boolean res = stream.packetEOFBody();
|
||||
if (!res) { // error adding EOFB previous packet too long
|
||||
packetExchange();
|
||||
stream.packetEOFBody();
|
||||
}
|
||||
}
|
||||
// Unknown errorcode yet, not sending: stream.packetEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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.obex;
|
||||
|
||||
import javax.obex.Authenticator;
|
||||
import javax.obex.SessionNotifier;
|
||||
import javax.obex.ServerRequestHandler;
|
||||
import javax.microedition.io.Connection;
|
||||
import java.io.IOException;
|
||||
|
||||
public class SessionNotifierImpl implements SessionNotifier {
|
||||
|
||||
private ObexTransportNotifier notifier;
|
||||
|
||||
public SessionNotifierImpl(ObexTransportNotifier notifier)
|
||||
throws IOException {
|
||||
this.notifier = notifier;
|
||||
}
|
||||
|
||||
public Connection acceptAndOpen(ServerRequestHandler handler)
|
||||
throws IOException {
|
||||
return acceptAndOpen(handler, null);
|
||||
}
|
||||
|
||||
public Connection acceptAndOpen(ServerRequestHandler handler,
|
||||
Authenticator auth) throws IOException {
|
||||
if (notifier == null) {
|
||||
throw new IOException("session closed");
|
||||
}
|
||||
if (handler == null) {
|
||||
throw new NullPointerException("null handler");
|
||||
}
|
||||
ObexTransport transport = notifier.acceptAndOpen();
|
||||
return new ServerConnectionImpl(transport, handler, auth);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (notifier != null) notifier.close();
|
||||
notifier = null;
|
||||
}
|
||||
|
||||
public Connection getTransport() {
|
||||
return notifier.getUnderlyingConnection();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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.obex.btgoep;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
import javax.microedition.io.StreamConnection;
|
||||
import javax.microedition.io.Connection;
|
||||
import com.sun.j2me.main.Configuration;
|
||||
import com.sun.jsr082.obex.ObexTransport;
|
||||
|
||||
/*
|
||||
* Provides underlying stream connection used as transport by shared obex
|
||||
* implementation.
|
||||
*/
|
||||
public class BTGOEPConnection implements ObexTransport {
|
||||
|
||||
private StreamConnection sock;
|
||||
private InputStream is;
|
||||
private OutputStream os;
|
||||
|
||||
/*
|
||||
* Create BTGOEPConnection
|
||||
* @param sock Stream connection for the transport layer
|
||||
*/
|
||||
protected BTGOEPConnection(StreamConnection sock) throws IOException {
|
||||
this.sock = sock;
|
||||
is = sock.openInputStream();
|
||||
os = sock.openOutputStream();
|
||||
}
|
||||
|
||||
/*
|
||||
* Closes connection as well as the input stream and
|
||||
* the output stream openning for this connection.
|
||||
* @throws IOException if I/O error.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
IOException ioe = null;
|
||||
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
ioe = e;
|
||||
}
|
||||
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
ioe = e;
|
||||
}
|
||||
|
||||
try {
|
||||
sock.close();
|
||||
} catch (IOException e) {
|
||||
ioe = e;
|
||||
}
|
||||
|
||||
// catch IOException if any of the above call has thrown one
|
||||
if (ioe != null) {
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads the packet data into specified buffer.
|
||||
* <p>
|
||||
* If the specified buffer length is 0, then 0 data
|
||||
* will be read into this buffer, and the rest of packet
|
||||
* data is lost.
|
||||
* <p>
|
||||
*
|
||||
* @param inData the data array to fill with received bytes.
|
||||
* @exception IOException if a local or remote connection
|
||||
* is closed or I/O error has happen.
|
||||
*
|
||||
* @exception NullPointerException if the specified buffer is null.
|
||||
*/
|
||||
public int read(byte[] inData) throws IOException {
|
||||
readFully(inData, 0, 3); // read header
|
||||
int packetLength = decodeLength16(inData, 1);
|
||||
if (packetLength < 3 || packetLength > inData.length) {
|
||||
throw new IOException("protocol error");
|
||||
}
|
||||
|
||||
readFully(inData, 3, packetLength - 3);
|
||||
return packetLength;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* @param outData the buffer with the data to be sent.
|
||||
* @param len the number of bytes to be sent.
|
||||
* @exception IOException if a local or remote connection
|
||||
* is closed or I/O error has happen.
|
||||
*
|
||||
* @exception NullPointerException if the specified buffer is null.
|
||||
*/
|
||||
public void write(byte[] outData, int len) throws IOException {
|
||||
os.write(outData, 0, len);
|
||||
os.flush();
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines the amount of data (maximum packet size) that can
|
||||
* be successfully sent in a single write operation. If the size
|
||||
* of data is greater than the maximum packet size, then then only
|
||||
* the first maximum packet size bytes of the packet are sent,
|
||||
* and the rest will be discarded.
|
||||
* <p>
|
||||
*
|
||||
* @return the maximum number of bytes that can be sent/received
|
||||
* in a single call to read()/ write() without losing any data.
|
||||
*/
|
||||
public int getMaximumPacketSize() {
|
||||
return Configuration.getIntProperty(
|
||||
"obex.packetLength.max", 4096);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads up to <code>len</code> bytes of data from the input stream into
|
||||
* an array of bytes.
|
||||
* @param array the buffer into which the data is read.
|
||||
* @param offset the start offset in array <code>b</code>
|
||||
* at which the data is written.
|
||||
* @param size the maximum number of bytes to read.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
private final void readFully(byte[] array, int offset, int size)
|
||||
throws IOException {
|
||||
while (size != 0) {
|
||||
int count = is.read(array, offset, size);
|
||||
if (count == -1) {
|
||||
throw new IOException("read error");
|
||||
}
|
||||
offset += count;
|
||||
size -= count;
|
||||
}
|
||||
}
|
||||
|
||||
private final int decodeLength16(byte[] buffer, int off) {
|
||||
return ((((int)buffer[off]) & 0xFF) << 8)
|
||||
+ (((int)buffer[off + 1]) & 0xFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get underlying connection.
|
||||
*/
|
||||
public Connection getUnderlyingConnection() {
|
||||
return sock;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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.obex.btgoep;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.bluetooth.*;
|
||||
import javax.microedition.io.StreamConnectionNotifier;
|
||||
import javax.microedition.io.StreamConnection;
|
||||
import javax.microedition.io.Connection;
|
||||
import com.sun.jsr082.bluetooth.ServiceRecordImpl;
|
||||
import com.sun.jsr082.obex.ObexTransportNotifier;
|
||||
import com.sun.jsr082.obex.ObexTransport;
|
||||
|
||||
/*
|
||||
* Provides underlying stream notifier to shared obex implementation.
|
||||
*/
|
||||
public class BTGOEPNotifier implements ObexTransportNotifier {
|
||||
|
||||
/* Keeps notifier for transport layer */
|
||||
private StreamConnectionNotifier notifier;
|
||||
|
||||
/* Keeps OBEX UUID for service record construction. */
|
||||
static public final DataElement DE_OBEX_UUID =
|
||||
new DataElement(DataElement.UUID, new UUID(0x0008));
|
||||
|
||||
/*
|
||||
* Create BTGOEP Notifier
|
||||
* @param notifier notifier for transport layer
|
||||
* @exception IOException if an error occured while service record
|
||||
* creation
|
||||
*/
|
||||
protected BTGOEPNotifier(StreamConnectionNotifier notifier)
|
||||
throws IOException {
|
||||
this.notifier = notifier;
|
||||
}
|
||||
|
||||
/*
|
||||
* Accepts client connection to the service this notifier is assigned to.
|
||||
*
|
||||
* @return connection to a client just accepted on transport layer.
|
||||
* @exception IOException if an error occured on transport layer.
|
||||
*/
|
||||
public ObexTransport acceptAndOpen() throws IOException {
|
||||
return createTransportConnection(
|
||||
(StreamConnection)(notifier.acceptAndOpen()));
|
||||
}
|
||||
|
||||
/*
|
||||
* Closes this notifier on the transport layer
|
||||
* @exception IOException if an error occured on transport layer
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
notifier.close();
|
||||
}
|
||||
|
||||
/*
|
||||
* Create btgoep transport connection.
|
||||
* @param sock transport connection
|
||||
* @return BTGOEP Connection
|
||||
*/
|
||||
protected BTGOEPConnection createTransportConnection(
|
||||
StreamConnection sock) throws IOException {
|
||||
return new BTGOEPConnection(sock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get transport connection noifier
|
||||
* @return transport notifier
|
||||
*/
|
||||
public Connection getUnderlyingConnection() {
|
||||
return notifier;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.bluetooth;
|
||||
import java.io.IOException;
|
||||
|
||||
/*
|
||||
* This class is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public class BluetoothConnectionException extends IOException {
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int UNKNOWN_PSM = 0x0001;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int SECURITY_BLOCK = 0x0002;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int NO_RESOURCES = 0x0003;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int FAILED_NOINFO = 0x0004;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int TIMEOUT = 0x0005;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int UNACCEPTABLE_PARAMS = 0x0006;
|
||||
|
||||
/* Contains the error code specified in constructor. */
|
||||
private int status;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public BluetoothConnectionException(int error) {
|
||||
this(error, null);
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public BluetoothConnectionException(int error, String msg) {
|
||||
super(msg);
|
||||
|
||||
switch (error) {
|
||||
case UNKNOWN_PSM: /* falls through */
|
||||
case SECURITY_BLOCK: /* falls through */
|
||||
case NO_RESOURCES: /* falls through */
|
||||
case FAILED_NOINFO: /* falls through */
|
||||
case TIMEOUT: /* falls through */
|
||||
case UNACCEPTABLE_PARAMS:
|
||||
status = error;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid error code: " + error);
|
||||
}
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
} // end of class 'BluetoothConnectionException' definition
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.bluetooth;
|
||||
import java.io.IOException;
|
||||
|
||||
/*
|
||||
* This class is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public class BluetoothStateException extends IOException {
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public BluetoothStateException() {
|
||||
super();
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public BluetoothStateException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
} // end of class 'BluetoothStateException' definition
|
|
@ -0,0 +1,356 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.bluetooth;
|
||||
import java.util.Vector;
|
||||
|
||||
/*
|
||||
* This class is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public class DataElement {
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int NULL = 0x0000;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int U_INT_1 = 0x0008;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int U_INT_2 = 0x0009;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int U_INT_4 = 0x000A;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int U_INT_8 = 0x000B;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int U_INT_16 = 0x000C;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int INT_1 = 0x0010;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int INT_2 = 0x0011;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int INT_4 = 0x0012;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int INT_8 = 0x0013;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int INT_16 = 0x0014;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int URL = 0x0040;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int UUID = 0x0018;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int BOOL = 0x0028;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int STRING = 0x0020;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int DATSEQ = 0x0030;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int DATALT = 0x0038;
|
||||
|
||||
/* Keeps the specified or derived type of the element. */
|
||||
private int valueType;
|
||||
|
||||
/* Keeps the boolean value for the type BOOL. */
|
||||
private boolean booleanValue;
|
||||
|
||||
/* Keeps the long value for the types *INT*. */
|
||||
private long longValue;
|
||||
|
||||
/*
|
||||
* Keeps the misc type value for the rest of types.
|
||||
*
|
||||
* This field also keeps the value for the type DATALT and DATSEQ.
|
||||
* In this case it's a Vector. The access to the Vector elements
|
||||
* is synchronized in cldc (according the source code). But,
|
||||
* this is not documented, so we make a synchronize access
|
||||
* to this field to fit any cldc implementation.
|
||||
*/
|
||||
private Object miscValue;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public DataElement(int valueType) {
|
||||
switch (valueType) {
|
||||
case NULL: /* miscValue = null in this case. */
|
||||
break;
|
||||
case DATALT: /* falls through */
|
||||
case DATSEQ:
|
||||
this.miscValue = new Vector();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid valueType for this constructor: " + valueType);
|
||||
}
|
||||
this.valueType = valueType;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public DataElement(boolean bool) {
|
||||
valueType = BOOL;
|
||||
booleanValue = bool;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public DataElement(int valueType, long value) {
|
||||
long min = 0;
|
||||
long max = 0;
|
||||
|
||||
switch (valueType) {
|
||||
case U_INT_1:
|
||||
max = 0xffL;
|
||||
break;
|
||||
case U_INT_2:
|
||||
max = 0xffffL;
|
||||
break;
|
||||
case U_INT_4:
|
||||
max = 0xffffffffL;
|
||||
break;
|
||||
case INT_1:
|
||||
min = Byte.MIN_VALUE;
|
||||
max = Byte.MAX_VALUE;
|
||||
break;
|
||||
case INT_2:
|
||||
min = -0x8000L;
|
||||
max = 0x7fffL;
|
||||
break;
|
||||
case INT_4:
|
||||
min = Integer.MIN_VALUE;
|
||||
max = Integer.MAX_VALUE;
|
||||
break;
|
||||
case INT_8:
|
||||
min = Long.MIN_VALUE;
|
||||
max = Long.MAX_VALUE;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid 'valueType' for this constructor: " + valueType);
|
||||
}
|
||||
|
||||
// check if value in the valid rangle for this type
|
||||
if (value < min || value > max) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid 'value' for specified type: " + value);
|
||||
}
|
||||
this.valueType = valueType;
|
||||
this.longValue = value;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public DataElement(int valueType, Object value) {
|
||||
boolean isCorrectValue = true;
|
||||
|
||||
switch (valueType) {
|
||||
case URL: /* falls through */
|
||||
case STRING:
|
||||
isCorrectValue = value instanceof String;
|
||||
break;
|
||||
case UUID:
|
||||
isCorrectValue = value instanceof UUID;
|
||||
break;
|
||||
case INT_16: /* falls through */
|
||||
case U_INT_16:
|
||||
isCorrectValue = value instanceof byte[]
|
||||
&& ((byte[]) value).length == 16;
|
||||
break;
|
||||
case U_INT_8:
|
||||
isCorrectValue = value instanceof byte[]
|
||||
&& ((byte[]) value).length == 8;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid 'valueType' for this constructor: " + valueType);
|
||||
}
|
||||
|
||||
// check if value in the valid rangle for this type
|
||||
if (!isCorrectValue) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid 'value' for specified type: " + value);
|
||||
}
|
||||
this.valueType = valueType;
|
||||
this.miscValue = value;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public synchronized void addElement(DataElement elem) {
|
||||
|
||||
/*
|
||||
* We can't optimize this by invoking the
|
||||
* this.insertElementAt(elem, getSize()), because
|
||||
* the ClassCastException may be thrown from getSize()
|
||||
* which gives us improper stack trace.
|
||||
*/
|
||||
if (valueType != DATSEQ && valueType != DATALT) {
|
||||
throw new ClassCastException(
|
||||
"Invalid element type for this method: " + valueType);
|
||||
}
|
||||
|
||||
if (elem == null) {
|
||||
throw new NullPointerException("Specified element is null");
|
||||
}
|
||||
((Vector) miscValue).addElement(elem);
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public synchronized void insertElementAt(DataElement elem, int index) {
|
||||
if (valueType != DATSEQ && valueType != DATALT) {
|
||||
throw new ClassCastException(
|
||||
"Invalid element type for this method: " + valueType);
|
||||
}
|
||||
|
||||
if (elem == null) {
|
||||
throw new NullPointerException("Specified element is null");
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't use the Vector.insertElementAt check for out of
|
||||
* bounds, because Vector throws ArrayIndexOutOfBoundsException
|
||||
* in this case.
|
||||
*/
|
||||
if (index < 0 || index > ((Vector) miscValue).size()) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Specified index is out of range");
|
||||
}
|
||||
((Vector) miscValue).insertElementAt(elem, index);
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public synchronized int getSize() {
|
||||
if (valueType != DATSEQ && valueType != DATALT) {
|
||||
throw new ClassCastException(
|
||||
"Invalid element type for this method: " + valueType);
|
||||
}
|
||||
return ((Vector) miscValue).size();
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean removeElement(DataElement elem) {
|
||||
if (valueType != DATSEQ && valueType != DATALT) {
|
||||
throw new ClassCastException(
|
||||
"Invalid element type for this method: " + valueType);
|
||||
}
|
||||
|
||||
if (elem == null) {
|
||||
throw new NullPointerException("Specified element is null");
|
||||
}
|
||||
|
||||
/*
|
||||
* The Bluetooth spec says the two DataElement equals if their
|
||||
* references are equal. According to cldc1.1 ref impl sources,
|
||||
* the Vector uses 'equals' call, and the Object.equls uses
|
||||
* a references compare, so we may not care about doing this here.
|
||||
*/
|
||||
return ((Vector) miscValue).removeElement(elem);
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int getDataType() {
|
||||
return valueType;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public long getLong() {
|
||||
switch (valueType) {
|
||||
case U_INT_1: /* falls through */
|
||||
case U_INT_2: /* falls through */
|
||||
case U_INT_4: /* falls through */
|
||||
case INT_1: /* falls through */
|
||||
case INT_2: /* falls through */
|
||||
case INT_4: /* falls through */
|
||||
case INT_8:
|
||||
break;
|
||||
default:
|
||||
throw new ClassCastException(
|
||||
"Invalid element type for this method: " + valueType);
|
||||
}
|
||||
return longValue;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean getBoolean() {
|
||||
if (valueType != BOOL) {
|
||||
throw new ClassCastException(
|
||||
"Invalid element type for this method: " + valueType);
|
||||
}
|
||||
return booleanValue;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public synchronized Object getValue() {
|
||||
Object retValue = miscValue;
|
||||
|
||||
/*
|
||||
* According to cldc & bluetooth specifications, the String and UUID
|
||||
* are immutable, so we may not return a clone object to safe
|
||||
* the stored one.
|
||||
*
|
||||
* The Vector.elements() returns an Enumeration, which does not allow
|
||||
* to break the Vector either.
|
||||
*
|
||||
* The array may be modified by reference, so we have to return
|
||||
* a clone.
|
||||
*/
|
||||
switch (valueType) {
|
||||
case URL: /* falls through */
|
||||
case STRING: /* falls through */
|
||||
case UUID:
|
||||
break;
|
||||
case DATALT: /* falls through */
|
||||
case DATSEQ:
|
||||
retValue = ((Vector) miscValue).elements();
|
||||
break;
|
||||
case U_INT_8: /* falls through */
|
||||
case U_INT_16: /* falls through */
|
||||
case INT_16:
|
||||
int length = ((byte[]) miscValue).length;
|
||||
retValue = new byte[length];
|
||||
System.arraycopy(miscValue, 0, retValue, 0, length);
|
||||
break;
|
||||
default:
|
||||
throw new ClassCastException(
|
||||
"Invalid element type for this method: " + valueType);
|
||||
}
|
||||
return retValue;
|
||||
}
|
||||
} // end of class 'DataElement' definition
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.bluetooth;
|
||||
|
||||
/*
|
||||
* This class is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public class DeviceClass {
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private int record;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private static final int MASK_MINOR = 0xFC;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private static final int MASK_MAJOR = 0x1F00;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private static final int MASK_SERVICE = 0xFFE000;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private static final int MASK_OVERFLOW = 0xFF000000;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public DeviceClass(int record) {
|
||||
if ((record & MASK_OVERFLOW) != 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The 'record' bits out of (0-23) range.");
|
||||
}
|
||||
this.record = record;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int getServiceClasses() {
|
||||
return record & MASK_SERVICE;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int getMajorDeviceClass() {
|
||||
return record & MASK_MAJOR;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int getMinorDeviceClass() {
|
||||
return record & MASK_MINOR;
|
||||
}
|
||||
} // end of class 'DeviceClass' definition
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.bluetooth;
|
||||
import com.sun.jsr082.bluetooth.DiscoveryAgentImpl;
|
||||
|
||||
/*
|
||||
* This class is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public class DiscoveryAgent {
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int NOT_DISCOVERABLE = 0;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int GIAC = 0x9E8B33;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int LIAC = 0x9E8B00;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int CACHED = 0x00;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int PREKNOWN = 0x01;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private DiscoveryAgentImpl discoveryAgentImpl;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
DiscoveryAgent() {
|
||||
discoveryAgentImpl = DiscoveryAgentImpl.getInstance();
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public RemoteDevice[] retrieveDevices(int option) {
|
||||
return discoveryAgentImpl.retrieveDevices(option);
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean startInquiry(int accessCode, DiscoveryListener listener)
|
||||
throws BluetoothStateException {
|
||||
return discoveryAgentImpl.startInquiry(accessCode, listener);
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean cancelInquiry(DiscoveryListener listener) {
|
||||
return discoveryAgentImpl.cancelInquiry(listener);
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int searchServices(int[] attrSet, UUID[] uuidSet, RemoteDevice btDev,
|
||||
DiscoveryListener discListener) throws BluetoothStateException {
|
||||
return discoveryAgentImpl.searchServices(attrSet, uuidSet, btDev,
|
||||
discListener);
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean cancelServiceSearch(int transID) {
|
||||
return discoveryAgentImpl.cancelServiceSearch(transID);
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public String selectService(UUID uuid, int security, boolean master)
|
||||
throws BluetoothStateException {
|
||||
return discoveryAgentImpl.selectService(uuid, security, master);
|
||||
}
|
||||
} // end of class 'DiscoveryAgent' definition
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.bluetooth;
|
||||
|
||||
/*
|
||||
* This interface is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public interface DiscoveryListener {
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int INQUIRY_COMPLETED = 0x00;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int INQUIRY_TERMINATED = 0x05;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int INQUIRY_ERROR = 0x07;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int SERVICE_SEARCH_COMPLETED = 0x01;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int SERVICE_SEARCH_TERMINATED = 0x02;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int SERVICE_SEARCH_ERROR = 0x03;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int SERVICE_SEARCH_NO_RECORDS = 0x04;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int SERVICE_SEARCH_DEVICE_NOT_REACHABLE = 0x06;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod);
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public void servicesDiscovered(int transID, ServiceRecord[] servRecord);
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public void serviceSearchCompleted(int transID, int respCode);
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public void inquiryCompleted(int discType);
|
||||
} // end of class 'DiscoveryListener' definition
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.bluetooth;
|
||||
import javax.microedition.io.Connection;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/*
|
||||
* This interface is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public interface L2CAPConnection extends Connection {
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int DEFAULT_MTU = 672;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int MINIMUM_MTU = 48;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int getTransmitMTU() throws IOException;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int getReceiveMTU() throws IOException;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public void send(byte[] data) throws IOException;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int receive(byte[] inBuf) throws IOException;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean ready() throws IOException;
|
||||
} // end of class 'L2CAPConnection' definition
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.bluetooth;
|
||||
import javax.microedition.io.Connection;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/*
|
||||
* This interface is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public interface L2CAPConnectionNotifier extends Connection {
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public L2CAPConnection acceptAndOpen() throws IOException;
|
||||
} // end of class 'L2CAPConnectionNotifier' definition
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.bluetooth;
|
||||
import com.sun.jsr082.bluetooth.LocalDeviceImpl;
|
||||
import javax.microedition.io.Connection;
|
||||
|
||||
/*
|
||||
* This class is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public class LocalDevice {
|
||||
|
||||
/* Keeps this singleton object. */
|
||||
private static LocalDevice localDevice;
|
||||
|
||||
static {
|
||||
try {
|
||||
localDevice = getLocalDevice();
|
||||
} catch (BluetoothStateException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/* Keeps the reference to implementation object. */
|
||||
private static LocalDeviceImpl localDeviceImpl;
|
||||
|
||||
/*
|
||||
* Keeps the discovery agen reference -
|
||||
* because the DiscoveryAgent.<init> is package private,
|
||||
* so it can't be created ffrom the implemetation.
|
||||
*/
|
||||
private DiscoveryAgent discoveryAgent;
|
||||
|
||||
/*
|
||||
* The default constructor is hidden so that no one can create a new
|
||||
* instance of the LocalDevice. To get the LocalDevice
|
||||
* object for this device, use the <code>getLocalDevice()</code>
|
||||
* static method in this class.
|
||||
*
|
||||
* @see #getLocalDevice
|
||||
*/
|
||||
private LocalDevice() {}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static LocalDevice getLocalDevice() throws BluetoothStateException {
|
||||
|
||||
/*
|
||||
* The method is not declared as synchronized to keep
|
||||
* its signature unchanged.
|
||||
*/
|
||||
synchronized (LocalDevice.class) {
|
||||
if (localDevice == null) {
|
||||
try {
|
||||
// create a shared impl object and 'this'
|
||||
localDevice = new LocalDevice();
|
||||
|
||||
/*
|
||||
* create a DiscoveryAgent from here.
|
||||
* This should be done one time only
|
||||
* regardless whether or not the system is
|
||||
* initialized for the first time.
|
||||
*
|
||||
* We suppose the getLocalDevice() may be called
|
||||
* for the next time if the first try failed.
|
||||
*/
|
||||
if (localDevice.discoveryAgent == null) {
|
||||
localDevice.discoveryAgent = new DiscoveryAgent();
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructing LocaldeviceImpl causes initialization
|
||||
* of device properties and attributes.
|
||||
*/
|
||||
localDeviceImpl = LocalDeviceImpl.getInstance();
|
||||
} catch (BluetoothStateException bse) {
|
||||
localDevice = null;
|
||||
throw bse;
|
||||
} catch (Throwable e) {
|
||||
localDevice = null;
|
||||
throw new BluetoothStateException(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return localDevice;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public DiscoveryAgent getDiscoveryAgent() {
|
||||
|
||||
/*
|
||||
* This is an only exception for the "API/IMPL wrapper"
|
||||
* scheme, i.e. the DiscoveryAgent object is stored
|
||||
* locally in this class.
|
||||
*/
|
||||
return discoveryAgent;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public String getFriendlyName() {
|
||||
return localDeviceImpl.getFriendlyName();
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public DeviceClass getDeviceClass() {
|
||||
return localDeviceImpl.getDeviceClass();
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static String getProperty(String property) {
|
||||
return localDevice != null ? localDeviceImpl.getProperty(property) :
|
||||
null;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int getDiscoverable() {
|
||||
return localDeviceImpl.getDiscoverable();
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public String getBluetoothAddress() {
|
||||
return localDeviceImpl.getBluetoothAddress();
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean setDiscoverable(int mode) throws BluetoothStateException {
|
||||
return localDeviceImpl.setDiscoverable(mode);
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public ServiceRecord getRecord(Connection notifier) {
|
||||
return localDeviceImpl.getRecord(notifier);
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public void updateRecord(ServiceRecord srvRecord)
|
||||
throws ServiceRegistrationException {
|
||||
localDeviceImpl.updateRecord(srvRecord);
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static boolean isPowerOn() {
|
||||
return localDeviceImpl.isPowerOn();
|
||||
}
|
||||
|
||||
} // end of class 'LocalDevice' definition
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.bluetooth;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.microedition.io.Connection;
|
||||
import com.sun.jsr082.bluetooth.BluetoothConnection;
|
||||
import com.sun.jsr082.bluetooth.BCC;
|
||||
|
||||
/*
|
||||
* This class is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public class RemoteDevice {
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private long l_address;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private String s_address;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private String friendlyName;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
protected RemoteDevice(String address) {
|
||||
if (address == null) {
|
||||
throw new NullPointerException("null address");
|
||||
}
|
||||
final String errorMsg = "Malformed address: " + address;
|
||||
|
||||
if (address.length() != 12) {
|
||||
throw new IllegalArgumentException(errorMsg);
|
||||
}
|
||||
|
||||
if (address.startsWith("-")) {
|
||||
throw new IllegalArgumentException(errorMsg);
|
||||
}
|
||||
|
||||
try {
|
||||
l_address = Long.parseLong(address, 16);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException(errorMsg);
|
||||
}
|
||||
|
||||
// should be upper case only
|
||||
address = address.toUpperCase();
|
||||
|
||||
try {
|
||||
String lAddr = LocalDevice.getLocalDevice().getBluetoothAddress();
|
||||
|
||||
if (address.equals(lAddr)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Can't use the local address.");
|
||||
}
|
||||
} catch (BluetoothStateException e) {
|
||||
throw new RuntimeException("Can't initialize bluetooth support");
|
||||
}
|
||||
s_address = address;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean isTrustedDevice() {
|
||||
return BCC.getInstance().isTrusted(getBluetoothAddress());
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public String getFriendlyName(boolean alwaysAsk) throws IOException {
|
||||
// contact the remote device if name is not known or alwaysAsk is true
|
||||
if (friendlyName == null || alwaysAsk) {
|
||||
friendlyName = BCC.getInstance().getFriendlyName(
|
||||
getBluetoothAddress());
|
||||
}
|
||||
return friendlyName;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public final String getBluetoothAddress() {
|
||||
return s_address;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof RemoteDevice &&
|
||||
l_address == ((RemoteDevice) obj).l_address;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int hashCode() {
|
||||
return (int) ((l_address >>> 24) ^ (l_address & 0xffffffL));
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static RemoteDevice getRemoteDevice(Connection conn)
|
||||
throws IOException {
|
||||
return BluetoothConnection.getConnection(conn).getRemoteDevice();
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean authenticate() throws IOException {
|
||||
if (!BCC.getInstance().isConnected(getBluetoothAddress())) {
|
||||
throw new IOException("There are no open connections between the " +
|
||||
"local device and this RemoteDevice.");
|
||||
}
|
||||
|
||||
return BCC.getInstance().authenticate(getBluetoothAddress());
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean authorize(Connection conn) throws IOException {
|
||||
BluetoothConnection btconn = BluetoothConnection.getConnection(conn);
|
||||
|
||||
if (!equals(btconn.getRemoteDevice())) {
|
||||
throw new IllegalArgumentException("The specified connection " +
|
||||
"is not a connection to this RemoteDevice.");
|
||||
}
|
||||
if (!btconn.isServerSide()) {
|
||||
throw new IllegalArgumentException("The local device is client " +
|
||||
"rather than the server for the specified connection.");
|
||||
}
|
||||
|
||||
return authenticate() && (isTrustedDevice() || btconn.isAuthorized() ||
|
||||
btconn.authorize());
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean encrypt(Connection conn, boolean on) throws IOException {
|
||||
BluetoothConnection btconn = BluetoothConnection.getConnection(conn);
|
||||
if (!equals(btconn.getRemoteDevice())) {
|
||||
throw new IllegalArgumentException("The specified connection " +
|
||||
"is not a connection to this RemoteDevice.");
|
||||
}
|
||||
if (on && !authenticate()) {
|
||||
return false;
|
||||
}
|
||||
return btconn.encrypt(on);
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean isAuthenticated() {
|
||||
return BCC.getInstance().isAuthenticated(getBluetoothAddress());
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean isAuthorized(Connection conn) throws IOException {
|
||||
BluetoothConnection btconn = BluetoothConnection.getConnection(conn);
|
||||
RemoteDevice device;
|
||||
|
||||
try {
|
||||
device = btconn.getRemoteDevice();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
if (!equals(device)) {
|
||||
throw new IllegalArgumentException("The specified connection " +
|
||||
"is not a connection to this RemoteDevice.");
|
||||
}
|
||||
|
||||
return btconn.isServerSide() && btconn.isAuthorized();
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean isEncrypted() {
|
||||
return BCC.getInstance().isEncrypted(getBluetoothAddress());
|
||||
}
|
||||
} // end of class 'RemoteDevice' definition
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.bluetooth;
|
||||
import java.io.IOException;
|
||||
|
||||
/*
|
||||
* This interface is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public interface ServiceRecord {
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int NOAUTHENTICATE_NOENCRYPT = 0;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int AUTHENTICATE_NOENCRYPT = 0x01;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int AUTHENTICATE_ENCRYPT = 0x02;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public DataElement getAttributeValue(int attrID);
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public RemoteDevice getHostDevice();
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int[] getAttributeIDs();
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean populateRecord(int[] attrIDs)
|
||||
throws IOException;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public String getConnectionURL(int requiredSecurity, boolean mustBeMaster);
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public void setDeviceServiceClasses(int classes);
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean setAttributeValue(int attrID, DataElement attrValue);
|
||||
} // end of class 'ServiceRecord' definition
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.bluetooth;
|
||||
import java.io.IOException;
|
||||
|
||||
/*
|
||||
* This class is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public class ServiceRegistrationException extends IOException {
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public ServiceRegistrationException() {
|
||||
super();
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public ServiceRegistrationException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
} // end of class 'ServiceRegistrationException' definition
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.bluetooth;
|
||||
|
||||
/*
|
||||
* This class is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public class UUID {
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private long highBits;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private long lowBits;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private static final long BASE_UUID_HIGHT = 0x1000L;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private static final long BASE_UUID_LOW = 0x800000805F9B34FBL;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private static final char[] digits = {
|
||||
'0', '1', '2', '3', '4', '5',
|
||||
'6', '7', '8', '9', 'A', 'B',
|
||||
'C', 'D', 'E', 'F'
|
||||
};
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public UUID(long uuidValue) {
|
||||
|
||||
// check the specified value is out of range
|
||||
if (uuidValue < 0 || uuidValue > 0xffffffffL) {
|
||||
throw new IllegalArgumentException(
|
||||
"The 'uuidValue' is out of [0, 2^32 - 1] range: "
|
||||
+ uuidValue);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a UUID from 16/32 bits value.
|
||||
*
|
||||
* 128_bit_value = 16_bit_value * 2^96 + Bluetooth_Base_UUID
|
||||
* 128_bit_value = 32_bit_value * 2^96 + Bluetooth_Base_UUID
|
||||
*
|
||||
* No need to check the "overflow/negative", because
|
||||
* uuidValue is 32 bits & BASE_UUID_HIGHT is 16 bits.
|
||||
*/
|
||||
highBits = (uuidValue << 32) | BASE_UUID_HIGHT;
|
||||
lowBits = BASE_UUID_LOW;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public UUID(String uuidValue, boolean shortUUID) {
|
||||
if (uuidValue == null) {
|
||||
throw new NullPointerException("Specified 'uuidValue' is null");
|
||||
}
|
||||
|
||||
/*
|
||||
* The zero length is double checked by the parsing operation,
|
||||
* but the NumberFormatException is thrown in that case -
|
||||
* we need IllegalArgumentException according to spec.
|
||||
*/
|
||||
if (uuidValue.length() == 0 || (shortUUID && uuidValue.length() > 8) ||
|
||||
uuidValue.length() > 32) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid length of specified 'uuidValue': "
|
||||
+ uuidValue.length());
|
||||
}
|
||||
|
||||
// check if sign character presents
|
||||
if (uuidValue.indexOf('-') != -1) {
|
||||
throw new NumberFormatException(
|
||||
"The '-' character is not allowed: " + uuidValue);
|
||||
}
|
||||
|
||||
/*
|
||||
* 16-bit or 32-bit UUID case.
|
||||
*/
|
||||
if (shortUUID) {
|
||||
|
||||
// this checks the format and may throw a NumberFormatException
|
||||
long val = Long.parseLong(uuidValue, 16);
|
||||
|
||||
/*
|
||||
* create a UUID from 16/32 bits value.
|
||||
*
|
||||
* No need to check the "overflow/negative", because
|
||||
* lVal is 32 bits & BASE_UUID_HIGHT is 16 bits.
|
||||
*/
|
||||
highBits = (val << 32) | BASE_UUID_HIGHT;
|
||||
lowBits = BASE_UUID_LOW;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 128-bit UUID case.
|
||||
*/
|
||||
highBits = 0x0L;
|
||||
|
||||
// simple case (optimization)
|
||||
if (uuidValue.length() < 16) {
|
||||
lowBits = Long.parseLong(uuidValue, 16);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to do a 32 bits parsing, because the
|
||||
* Long.parseLong("ffff ffff ffff ffff") does not
|
||||
* parse such an unsigned number.
|
||||
*/
|
||||
int l = uuidValue.length();
|
||||
lowBits = Long.parseLong(uuidValue.substring(l - 8), 16);
|
||||
lowBits |= (Long.parseLong(uuidValue.substring(l - 16, l - 8), 16)
|
||||
<< 32);
|
||||
|
||||
if (l == 16) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (l <= 24) {
|
||||
highBits = Long.parseLong(uuidValue.substring(0, l - 16), 16);
|
||||
} else {
|
||||
highBits = Long.parseLong(uuidValue.substring(l - 24, l - 16), 16);
|
||||
highBits |= (Long.parseLong(uuidValue.substring(0, l - 24), 16)
|
||||
<< 32);
|
||||
}
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public String toString() {
|
||||
|
||||
/*
|
||||
* This implementation is taken from cldc1.1 Integer#toUnsignedString
|
||||
* one. The implementation which uses Integer#toHexString() is
|
||||
* 2-3 times slower, so such a code duplication is required here.
|
||||
*/
|
||||
int[] ints = new int[] {
|
||||
(int) (lowBits & 0xffffffffL),
|
||||
(int) (lowBits >>> 32 & 0xffffffffL),
|
||||
(int) (highBits & 0xffffffffL),
|
||||
(int) (highBits >>> 32 & 0xffffffffL)
|
||||
};
|
||||
int charPos = 32;
|
||||
char[] buf = new char[charPos];
|
||||
int shift = 4;
|
||||
int mask = 0xf;
|
||||
int needZerosIndex = -1;
|
||||
|
||||
/*
|
||||
* check with part of value requires the zero characters.
|
||||
*
|
||||
* I.e. the original algorithm gives as an 1 character
|
||||
* for the value '1', but we may want 00000001.
|
||||
*/
|
||||
for (int i = 3; i >= 0; i--) {
|
||||
if (ints[i] != 0) {
|
||||
needZerosIndex = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Process parts of UUID from low parts to high ones.
|
||||
*/
|
||||
for (int i = 0; i < ints.length; i++) {
|
||||
|
||||
/*
|
||||
* The 16 bits are zero & no need to fill with 0,
|
||||
* and it's not a UUID with value '0' (i != 0).
|
||||
*/
|
||||
if (ints[i] == 0 && needZerosIndex < i && i != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = 0; j < 8; j++) {
|
||||
buf[--charPos] = digits[ints[i] & mask];
|
||||
ints[i] >>>= shift;
|
||||
}
|
||||
}
|
||||
return new String(buf, charPos, (32 - charPos));
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public boolean equals(Object value) {
|
||||
return value instanceof UUID &&
|
||||
lowBits == ((UUID) value).lowBits &&
|
||||
highBits == ((UUID) value).highBits;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int hashCode() {
|
||||
return (int) (highBits ^ highBits >> 32 ^ lowBits ^ lowBits >> 32);
|
||||
}
|
||||
} // end of class 'UUID' definition
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.obex;
|
||||
|
||||
/*
|
||||
* This interface is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public interface Authenticator {
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public PasswordAuthentication onAuthenticationChallenge(String description,
|
||||
boolean isUserIdRequired, boolean isFullAccess);
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public byte[] onAuthenticationResponse(byte[] userName);
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.obex;
|
||||
import java.io.IOException;
|
||||
|
||||
/*
|
||||
* This interface is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public interface HeaderSet {
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int COUNT = 0xC0;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int NAME = 0x01;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int TYPE = 0x42;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int LENGTH = 0xC3;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int TIME_ISO_8601 = 0x44;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int TIME_4_BYTE = 0xC4;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int DESCRIPTION = 0x05;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int TARGET = 0x46;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int HTTP = 0x47;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int WHO = 0x4A;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBJECT_CLASS = 0x4F;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int APPLICATION_PARAMETER = 0x4C;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public void setHeader(int headerID, Object headerValue);
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public Object getHeader(int headerID) throws IOException;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int[] getHeaderList() throws IOException;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public void createAuthenticationChallenge(String realm, boolean userID,
|
||||
boolean access);
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int getResponseCode() throws IOException;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.obex;
|
||||
import java.io.IOException;
|
||||
import javax.microedition.io.ContentConnection;
|
||||
|
||||
/*
|
||||
* This interface is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public interface Operation extends ContentConnection {
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public void abort() throws IOException;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public HeaderSet getReceivedHeaders() throws IOException;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public void sendHeaders(HeaderSet headers) throws IOException;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int getResponseCode() throws IOException;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.obex;
|
||||
|
||||
/*
|
||||
* This class is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public class PasswordAuthentication {
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private byte[] password;
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private byte[] userName;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public PasswordAuthentication(byte[] userName, byte[] password) {
|
||||
if (password == null) {
|
||||
throw new NullPointerException("null password");
|
||||
}
|
||||
this.password = password;
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public byte[] getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public byte[] getPassword() {
|
||||
return password;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.obex;
|
||||
|
||||
/*
|
||||
* This class is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public class ResponseCodes {
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_OK = 0xA0;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_CREATED = 0xA1;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_ACCEPTED = 0xA2;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_NOT_AUTHORITATIVE = 0xA3;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_NO_CONTENT = 0xA4;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_RESET = 0xA5;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_PARTIAL = 0xA6;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_MULT_CHOICE = 0xB0;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_MOVED_PERM = 0xB1;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_MOVED_TEMP = 0xB2;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_SEE_OTHER = 0xB3;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_NOT_MODIFIED = 0xB4;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_USE_PROXY = 0xB5;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_BAD_REQUEST = 0xC0;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_UNAUTHORIZED = 0xC1;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_PAYMENT_REQUIRED = 0xC2;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_FORBIDDEN = 0xC3;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_NOT_FOUND = 0xC4;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_BAD_METHOD = 0xC5;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_NOT_ACCEPTABLE = 0xC6;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_PROXY_AUTH = 0xC7;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_TIMEOUT = 0xC8;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_CONFLICT = 0xC9;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_GONE = 0xCA;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_LENGTH_REQUIRED = 0xCB;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_PRECON_FAILED = 0xCC;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_ENTITY_TOO_LARGE = 0xCD;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_REQ_TOO_LARGE = 0xCE;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_UNSUPPORTED_TYPE = 0xCF;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_INTERNAL_ERROR = 0xD0;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_NOT_IMPLEMENTED = 0xD1;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_BAD_GATEWAY = 0xD2;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_UNAVAILABLE = 0xD3;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_GATEWAY_TIMEOUT = 0xD4;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_HTTP_VERSION = 0xD5;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_DATABASE_FULL = 0xE0;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public static final int OBEX_DATABASE_LOCKED = 0xE1;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private ResponseCodes() {}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.obex;
|
||||
import com.sun.jsr082.obex.HeaderSetImpl;
|
||||
|
||||
/*
|
||||
* This class is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public class ServerRequestHandler {
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
private long connId;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
protected ServerRequestHandler() {
|
||||
connId = -1;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public final HeaderSet createHeaderSet() {
|
||||
return new HeaderSetImpl(HeaderSetImpl.OWNER_SERVER_USER);
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public void setConnectionID(long id) {
|
||||
if (id < -1L || id > 0xFFFFFFFFL) {
|
||||
throw new IllegalArgumentException("invalid id");
|
||||
}
|
||||
connId = id;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public long getConnectionID() {
|
||||
return connId;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int onConnect(HeaderSet request, HeaderSet reply) {
|
||||
return ResponseCodes.OBEX_HTTP_OK;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public void onDisconnect(HeaderSet request, HeaderSet reply) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int onSetPath(HeaderSet request, HeaderSet reply, boolean backup,
|
||||
boolean create) {
|
||||
return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int onDelete(HeaderSet request, HeaderSet reply) {
|
||||
return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int onPut(Operation op) {
|
||||
return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public int onGet(Operation op) {
|
||||
return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public void onAuthenticationFailure(byte[] userName) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
|
||||
* Reserved. Use is subject to license terms.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
|
||||
*/
|
||||
package javax.obex;
|
||||
import java.io.IOException;
|
||||
import javax.microedition.io.Connection;
|
||||
|
||||
/*
|
||||
* This interface is defined by the JSR-82 specification
|
||||
* <em>Java™ APIs for Bluetooth™ Wireless Technology,
|
||||
* Version 1.1.</em>
|
||||
*/
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public interface SessionNotifier extends Connection {
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public Connection acceptAndOpen(ServerRequestHandler handler)
|
||||
throws IOException;
|
||||
|
||||
// JAVADOC COMMENT ELIDED
|
||||
public Connection acceptAndOpen(ServerRequestHandler handler,
|
||||
Authenticator auth) throws IOException;
|
||||
}
|
Загрузка…
Ссылка в новой задаче