/*********************************************************************************
* TotalCross Software Development Kit *
* Copyright (C) 2000-2012 SuperWaba Ltda. *
* All Rights Reserved *
* *
* This library and virtual machine 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. *
* *
* This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 *
* A copy of this license is located in file license.txt at the root of this *
* SDK or can be downloaded here: *
* http://www.gnu.org/licenses/lgpl-3.0.txt *
* *
*********************************************************************************/
package totalcross.io.device.bluetooth;
import totalcross.io.IOException;
import totalcross.sys.Convert;
import totalcross.util.Hashtable;
public class ServiceRecord4D
{
private Hashtable attributes = new Hashtable(5);
private RemoteDevice hostDevice;
Object nativeInstance;
private String baseURL;
public static final int NOAUTHENTICATE_NOENCRYPT = 0x00;
public static final int AUTHENTICATE_NOENCRYPT = 0x01;
public static final int AUTHENTICATE_ENCRYPT = 0x02;
ServiceRecord4D()
{
}
ServiceRecord4D(Object nativeInstance)
{
this.nativeInstance = nativeInstance;
}
public RemoteDevice getHostDevice()
{
return hostDevice;
}
public DataElement getAttributeValue(int attrID)
{
if (attrID < 0x0000 || attrID > 0xffff)
throw new IllegalArgumentException();
return (DataElement) attributes.get(attrID);
}
public String getConnectionURL()
{
return getConnectionURL(NOAUTHENTICATE_NOENCRYPT, false);
}
public String getConnectionURL(int requiredSecurity, boolean mustBeMaster)
{
if (baseURL == null)
{
int commChannel = -1;
DataElement protocolDescriptor = getAttributeValue(BluetoothConsts4D.ProtocolDescriptorList);
if ((protocolDescriptor == null) || (protocolDescriptor.getDataType() != DataElement.DATSEQ))
return null;
// get RFCOMM Channel ProtocolDescriptorList is DATSEQ of DATSEQ of UUID and optional parameters
boolean isL2CAP = false;
boolean isRFCOMM = false;
boolean isOBEX = false;
DataElement[] protocolsSeqEnum = (DataElement[]) protocolDescriptor.getValue();
for (int i = 0; i < protocolsSeqEnum.length; i++)
{
DataElement elementSeq = (DataElement) protocolsSeqEnum[i];
if (elementSeq.getDataType() != DataElement.DATSEQ) // skip if it's not a DATSEQ
continue;
DataElement[] elementSeqEnum = (DataElement[]) elementSeq.getValue();
if (elementSeqEnum.length <= 0) // skip if empty
continue;
DataElement protocolElement = (DataElement) elementSeqEnum[0];
if (protocolElement.getDataType() != DataElement.UUID) // skip if it's not UUID
continue;
Object uuid = protocolElement.getValue();
if (BluetoothConsts4D.OBEX_PROTOCOL_UUID.equals(uuid))
{
isOBEX = true;
isRFCOMM = false;
isL2CAP = false;
}
else if (elementSeqEnum.length > 1 && (BluetoothConsts4D.RFCOMM_PROTOCOL_UUID.equals(uuid)))
{
DataElement protocolPSMElement = (DataElement) elementSeqEnum[1];
switch (protocolPSMElement.getDataType())
{
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:
long val = protocolPSMElement.getLong();
if ((val >= BluetoothConsts4D.RFCOMM_CHANNEL_MIN) && (val <= BluetoothConsts4D.RFCOMM_CHANNEL_MAX))
{
commChannel = (int) val;
isRFCOMM = true;
isL2CAP = false;
}
break;
}
}
else if (elementSeqEnum.length > 1 && (BluetoothConsts4D.L2CAP_PROTOCOL_UUID.equals(uuid)))
{
DataElement protocolPSMElement = (DataElement) elementSeqEnum[1];
switch (protocolPSMElement.getDataType())
{
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:
long pcm = protocolPSMElement.getLong();
if ((pcm >= BluetoothConsts4D.L2CAP_PSM_MIN) && (pcm <= BluetoothConsts4D.L2CAP_PSM_MAX))
{
commChannel = (int) pcm;
isL2CAP = true;
}
break;
}
}
}
// check if the fields we need were set.
if (commChannel == -1)
return null;
if (!isOBEX && !isRFCOMM && !isL2CAP)
return null;
// start the url
StringBuffer urlBuffer = new StringBuffer();
urlBuffer.append(isOBEX ? BluetoothConsts4D.PROTOCOL_SCHEME_BT_OBEX : (isRFCOMM ? BluetoothConsts4D.PROTOCOL_SCHEME_RFCOMM : BluetoothConsts4D.PROTOCOL_SCHEME_L2CAP));
urlBuffer.append("://").append(hostDevice.address).append(":");
// comm channel
if (isL2CAP)
urlBuffer.append(Convert.zeroPad(Convert.toString(commChannel, 16), 4));
else
urlBuffer.append(commChannel);
baseURL = urlBuffer.toString();
}
String url = baseURL;
// security
switch (requiredSecurity)
{
case NOAUTHENTICATE_NOENCRYPT:
url += ";authenticate=false;encrypt=false";
break;
case AUTHENTICATE_NOENCRYPT:
url += ";authenticate=true;encrypt=false";
break;
case AUTHENTICATE_ENCRYPT:
url += ";authenticate=true;encrypt=true";
break;
default:
throw new IllegalArgumentException();
}
// master
return url + (mustBeMaster ? ";master=true" : ";master=false");
}
boolean readSDP4D(RemoteDevice device, byte[] input, int[] attrIDs) throws IOException
{
this.hostDevice = device;
boolean anyRetrieved = false;
DataElement element = new SDPInputStream(input).readElement();
DataElement[] elements = (DataElement[]) element.getValue();
for (int i = 0; i < elements.length; i += 2)
{
int attrID = (int) elements[i].getLong();
populateAttributeValue(attrID, elements[i + 1]);
if (!anyRetrieved)
{
for (int j = attrIDs.length - 1; j >= 0; j--)
{
if (attrIDs[j] == attrID)
{
anyRetrieved = true;
break;
}
}
}
}
return anyRetrieved;
}
void populateAttributeValue(int attrID, DataElement attrValue)
{
if (attrID < 0x0000 || attrID > 0xffff)
throw new IllegalArgumentException();
if (attrValue == null)
attributes.remove(attrID);
else
attributes.put(attrID, attrValue);
}
}