Add bluetooth classes and remove the custom LocalDevice class

This commit is contained in:
Marco Castelluccio 2014-11-19 12:35:27 +01:00
Родитель 915f028465
Коммит 2aab4c9604
83 изменённых файлов: 16642 добавлений и 8 удалений

Просмотреть файл

@ -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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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&trade; APIs for Bluetooth&trade; 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;
}