/**
* BlueCove - Java library for Bluetooth
* Copyright (C) 2006-2009 Vlad Skarzhevskyy
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* @author vlads
* @version $Id$
*/
package com.intel.bluetooth;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DataElement;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.ServiceRegistrationException;
import javax.microedition.io.Connection;
/**
*
*/
abstract class BluetoothConnectionNotifierBase implements Connection, BluetoothConnectionNotifierServiceRecordAccess {
private static Hashtable stackConnections = new Hashtable();
protected BluetoothStack bluetoothStack;
protected volatile long handle;
protected ServiceRecordImpl serviceRecord;
protected boolean closed;
protected int securityOpt;
static void shutdownConnections(BluetoothStack bluetoothStack) {
Vector connections;
synchronized (stackConnections) {
connections = (Vector) stackConnections.get(bluetoothStack);
}
if (connections == null) {
return;
}
Vector c2shutdown = new Vector();
c2shutdown = Utils.clone(connections.elements());
for (Enumeration en = c2shutdown.elements(); en.hasMoreElements();) {
BluetoothConnectionNotifierBase c = (BluetoothConnectionNotifierBase) en.nextElement();
try {
c.shutdown();
} catch (IOException e) {
DebugLog.debug("connection shutdown", e);
}
}
}
protected BluetoothConnectionNotifierBase(BluetoothStack bluetoothStack, BluetoothConnectionNotifierParams params)
throws BluetoothStateException, Error {
this.bluetoothStack = bluetoothStack;
this.closed = false;
if (params.name == null) {
throw new NullPointerException("Service name is null");
}
/*
* create service record to be later updated by BluetoothStack
*/
this.serviceRecord = new ServiceRecordImpl(this.bluetoothStack, null, 0);
}
protected void connectionCreated() {
Vector connections;
synchronized (stackConnections) {
connections = (Vector) stackConnections.get(this.bluetoothStack);
if (connections == null) {
connections = new Vector();
stackConnections.put(this.bluetoothStack, connections);
}
}
connections.addElement(this);
}
protected abstract void stackServerClose(long handle) throws IOException;
/*
* Close the connection. When a connection has been closed, access to any of
* its methods except this close() will cause an an IOException to be
* thrown. Closing an already closed connection has no effect. Streams
* derived from the connection may be open when method is called. Any open
* streams will cause the connection to be held open until they themselves
* are closed. In this latter case access to the open streams is permitted,
* but access to the connection is not.
*/
/*
* (non-Javadoc)
*
* @see javax.microedition.io.Connection#close()
*/
public void close() throws IOException {
if (!closed) {
shutdown();
}
}
public void shutdown() throws IOException {
closed = true;
if (handle != 0) {
DebugLog.debug("closing ConnectionNotifier", handle);
Vector connections;
synchronized (stackConnections) {
connections = (Vector) stackConnections.get(this.bluetoothStack);
}
connections.removeElement(this);
long synchronizedHandle;
synchronized (this) {
synchronizedHandle = handle;
handle = 0;
}
if (synchronizedHandle != 0) {
ServiceRecordsRegistry.unregister(serviceRecord);
if ((serviceRecord.deviceServiceClasses != 0)
&& ((bluetoothStack.getFeatureSet() & BluetoothStack.FEATURE_SET_DEVICE_SERVICE_CLASSES) != 0)) {
bluetoothStack.setLocalDeviceServiceClasses(ServiceRecordsRegistry.getDeviceServiceClasses());
}
stackServerClose(synchronizedHandle);
}
}
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothConnectionNotifierServiceRecordAccess#getServiceRecord()
*/
public ServiceRecord getServiceRecord() {
if (closed) {
throw new IllegalArgumentException("ConnectionNotifier is closed");
}
ServiceRecordsRegistry.register(this, serviceRecord);
return serviceRecord;
}
protected void validateServiceRecord(ServiceRecord srvRecord) {
DataElement protocolDescriptor = srvRecord.getAttributeValue(BluetoothConsts.ProtocolDescriptorList);
if ((protocolDescriptor == null) || (protocolDescriptor.getDataType() != DataElement.DATSEQ)) {
throw new IllegalArgumentException("ProtocolDescriptorList is mandatory");
}
DataElement serviceClassIDList = srvRecord.getAttributeValue(BluetoothConsts.ServiceClassIDList);
if ((serviceClassIDList == null) || (serviceClassIDList.getDataType() != DataElement.DATSEQ)
|| serviceClassIDList.getSize() == 0) {
throw new IllegalArgumentException("ServiceClassIDList is mandatory");
}
boolean isL2CAPpresent = false;
for (Enumeration protocolsSeqEnum = (Enumeration) protocolDescriptor.getValue(); protocolsSeqEnum
.hasMoreElements();) {
DataElement elementSeq = (DataElement) protocolsSeqEnum.nextElement();
if (elementSeq.getDataType() == DataElement.DATSEQ) {
Enumeration elementSeqEnum = (Enumeration) elementSeq.getValue();
if (elementSeqEnum.hasMoreElements()) {
DataElement protocolElement = (DataElement) elementSeqEnum.nextElement();
if ((protocolElement.getDataType() == DataElement.UUID)
&& (BluetoothConsts.L2CAP_PROTOCOL_UUID.equals(protocolElement.getValue()))) {
isL2CAPpresent = true;
break;
}
}
}
}
if (!isL2CAPpresent) {
throw new IllegalArgumentException("L2CAP UUID is mandatory in ProtocolDescriptorList");
}
}
protected abstract void updateStackServiceRecord(ServiceRecordImpl serviceRecord, boolean acceptAndOpen)
throws ServiceRegistrationException;
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothConnectionNotifierServiceRecordAccess#updateServiceRecord(boolean)
*/
public void updateServiceRecord(boolean acceptAndOpen) throws ServiceRegistrationException {
if (serviceRecord.attributeUpdated || (!acceptAndOpen)) {
try {
validateServiceRecord(this.serviceRecord);
} catch (IllegalArgumentException e) {
if (acceptAndOpen) {
throw new ServiceRegistrationException(e.getMessage());
} else {
throw e;
}
}
try {
updateStackServiceRecord(serviceRecord, acceptAndOpen);
} finally {
serviceRecord.attributeUpdated = false;
}
}
if ((serviceRecord.deviceServiceClasses != serviceRecord.deviceServiceClassesRegistered)
&& ((bluetoothStack.getFeatureSet() & BluetoothStack.FEATURE_SET_DEVICE_SERVICE_CLASSES) != 0)) {
bluetoothStack.setLocalDeviceServiceClasses(ServiceRecordsRegistry.getDeviceServiceClasses());
serviceRecord.deviceServiceClassesRegistered = serviceRecord.deviceServiceClasses;
}
}
}