View Javadoc

1   /**
2    *  BlueCove - Java library for Bluetooth
3    *  Copyright (C) 2006-2008 Vlad Skarzhevskyy
4    *
5    *  Licensed to the Apache Software Foundation (ASF) under one
6    *  or more contributor license agreements.  See the NOTICE file
7    *  distributed with this work for additional information
8    *  regarding copyright ownership.  The ASF licenses this file
9    *  to you under the Apache License, Version 2.0 (the
10   *  "License"); you may not use this file except in compliance
11   *  with the License.  You may obtain a copy of the License at
12   *
13   *    http://www.apache.org/licenses/LICENSE-2.0
14   *
15   *  Unless required by applicable law or agreed to in writing,
16   *  software distributed under the License is distributed on an
17   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18   *  KIND, either express or implied.  See the License for the
19   *  specific language governing permissions and limitations
20   *  under the License.
21   *
22   *  @author vlads
23   *  @version $Id: BluetoothConnectionNotifierBase.java 2476 2008-12-01 17:41:59Z skarzhevskyy $
24   */
25  package com.intel.bluetooth;
26  
27  import java.io.IOException;
28  import java.util.Enumeration;
29  import java.util.Hashtable;
30  import java.util.Vector;
31  
32  import javax.bluetooth.BluetoothStateException;
33  import javax.bluetooth.DataElement;
34  import javax.bluetooth.ServiceRecord;
35  import javax.bluetooth.ServiceRegistrationException;
36  import javax.microedition.io.Connection;
37  
38  /**
39   *
40   */
41  abstract class BluetoothConnectionNotifierBase implements Connection, BluetoothConnectionNotifierServiceRecordAccess {
42  
43  	private static Hashtable stackConnections = new Hashtable();
44  
45  	protected BluetoothStack bluetoothStack;
46  
47  	protected volatile long handle;
48  
49  	protected ServiceRecordImpl serviceRecord;
50  
51  	protected boolean closed;
52  
53  	protected int securityOpt;
54  
55  	static void shutdownConnections(BluetoothStack bluetoothStack) {
56  		Vector connections;
57  		synchronized (stackConnections) {
58  			connections = (Vector) stackConnections.get(bluetoothStack);
59  		}
60  		if (connections == null) {
61  			return;
62  		}
63  		Vector c2shutdown = new Vector();
64  		c2shutdown = Utils.clone(connections.elements());
65  		for (Enumeration en = c2shutdown.elements(); en.hasMoreElements();) {
66  			BluetoothConnectionNotifierBase c = (BluetoothConnectionNotifierBase) en.nextElement();
67  			try {
68  				c.shutdown();
69  			} catch (IOException e) {
70  				DebugLog.debug("connection shutdown", e);
71  			}
72  		}
73  	}
74  
75  	protected BluetoothConnectionNotifierBase(BluetoothStack bluetoothStack, BluetoothConnectionNotifierParams params)
76  			throws BluetoothStateException, Error {
77  		this.bluetoothStack = bluetoothStack;
78  		this.closed = false;
79  		if (params.name == null) {
80  			throw new NullPointerException("Service name is null");
81  		}
82  		/*
83  		 * create service record to be later updated by BluetoothStack
84  		 */
85  		this.serviceRecord = new ServiceRecordImpl(this.bluetoothStack, null, 0);
86  	}
87  
88  	protected void connectionCreated() {
89  		Vector connections;
90  		synchronized (stackConnections) {
91  			connections = (Vector) stackConnections.get(this.bluetoothStack);
92  			if (connections == null) {
93  				connections = new Vector();
94  				stackConnections.put(this.bluetoothStack, connections);
95  			}
96  		}
97  		connections.addElement(this);
98  	}
99  
100 	protected abstract void stackServerClose(long handle) throws IOException;
101 
102 	/*
103 	 * Close the connection. When a connection has been closed, access to any of
104 	 * its methods except this close() will cause an an IOException to be
105 	 * thrown. Closing an already closed connection has no effect. Streams
106 	 * derived from the connection may be open when method is called. Any open
107 	 * streams will cause the connection to be held open until they themselves
108 	 * are closed. In this latter case access to the open streams is permitted,
109 	 * but access to the connection is not.
110 	 */
111 
112 	/*
113 	 * (non-Javadoc)
114 	 *
115 	 * @see javax.microedition.io.Connection#close()
116 	 */
117 	public void close() throws IOException {
118 		if (!closed) {
119 			shutdown();
120 		}
121 	}
122 
123 	public void shutdown() throws IOException {
124 		closed = true;
125 		if (handle != 0) {
126 			DebugLog.debug("closing ConnectionNotifier", handle);
127 			Vector connections;
128 			synchronized (stackConnections) {
129 				connections = (Vector) stackConnections.get(this.bluetoothStack);
130 			}
131 			connections.removeElement(this);
132 			long synchronizedHandle;
133 			synchronized (this) {
134 				synchronizedHandle = handle;
135 				handle = 0;
136 			}
137 			if (synchronizedHandle != 0) {
138 
139 				ServiceRecordsRegistry.unregister(serviceRecord);
140 
141 				if ((serviceRecord.deviceServiceClasses != 0)
142 						&& ((bluetoothStack.getFeatureSet() & BluetoothStack.FEATURE_SET_DEVICE_SERVICE_CLASSES) != 0)) {
143 					bluetoothStack.setLocalDeviceServiceClasses(ServiceRecordsRegistry.getDeviceServiceClasses());
144 				}
145 
146 				stackServerClose(synchronizedHandle);
147 			}
148 		}
149 	}
150 
151 	/*
152 	 * (non-Javadoc)
153 	 *
154 	 * @see com.intel.bluetooth.BluetoothConnectionNotifierServiceRecordAccess#getServiceRecord()
155 	 */
156 	public ServiceRecord getServiceRecord() {
157 		if (closed) {
158 			throw new IllegalArgumentException("ConnectionNotifier is closed");
159 		}
160 		ServiceRecordsRegistry.register(this, serviceRecord);
161 		return serviceRecord;
162 	}
163 
164 	protected void validateServiceRecord(ServiceRecord srvRecord) {
165 		DataElement protocolDescriptor = srvRecord.getAttributeValue(BluetoothConsts.ProtocolDescriptorList);
166 		if ((protocolDescriptor == null) || (protocolDescriptor.getDataType() != DataElement.DATSEQ)) {
167 			throw new IllegalArgumentException("ProtocolDescriptorList is mandatory");
168 		}
169 
170 		DataElement serviceClassIDList = srvRecord.getAttributeValue(BluetoothConsts.ServiceClassIDList);
171 		if ((serviceClassIDList == null) || (serviceClassIDList.getDataType() != DataElement.DATSEQ)
172 				|| serviceClassIDList.getSize() == 0) {
173 			throw new IllegalArgumentException("ServiceClassIDList is mandatory");
174 		}
175 
176 		boolean isL2CAPpresent = false;
177 		for (Enumeration protocolsSeqEnum = (Enumeration) protocolDescriptor.getValue(); protocolsSeqEnum
178 				.hasMoreElements();) {
179 			DataElement elementSeq = (DataElement) protocolsSeqEnum.nextElement();
180 			if (elementSeq.getDataType() == DataElement.DATSEQ) {
181 				Enumeration elementSeqEnum = (Enumeration) elementSeq.getValue();
182 				if (elementSeqEnum.hasMoreElements()) {
183 					DataElement protocolElement = (DataElement) elementSeqEnum.nextElement();
184 					if ((protocolElement.getDataType() == DataElement.UUID)
185 							&& (BluetoothConsts.L2CAP_PROTOCOL_UUID.equals(protocolElement.getValue()))) {
186 						isL2CAPpresent = true;
187 						break;
188 					}
189 				}
190 			}
191 		}
192 		if (!isL2CAPpresent) {
193 			throw new IllegalArgumentException("L2CAP UUID is mandatory in ProtocolDescriptorList");
194 		}
195 	}
196 
197 	protected abstract void updateStackServiceRecord(ServiceRecordImpl serviceRecord, boolean acceptAndOpen)
198 			throws ServiceRegistrationException;
199 
200 	/*
201 	 * (non-Javadoc)
202 	 *
203 	 * @see com.intel.bluetooth.BluetoothConnectionNotifierServiceRecordAccess#updateServiceRecord(boolean)
204 	 */
205 	public void updateServiceRecord(boolean acceptAndOpen) throws ServiceRegistrationException {
206 		if (serviceRecord.attributeUpdated || (!acceptAndOpen)) {
207 			try {
208 				validateServiceRecord(this.serviceRecord);
209 			} catch (IllegalArgumentException e) {
210 				if (acceptAndOpen) {
211 					throw new ServiceRegistrationException(e.getMessage());
212 				} else {
213 					throw e;
214 				}
215 			}
216 			try {
217 				updateStackServiceRecord(serviceRecord, acceptAndOpen);
218 			} finally {
219 				serviceRecord.attributeUpdated = false;
220 			}
221 		}
222 		if ((serviceRecord.deviceServiceClasses != serviceRecord.deviceServiceClassesRegistered)
223 				&& ((bluetoothStack.getFeatureSet() & BluetoothStack.FEATURE_SET_DEVICE_SERVICE_CLASSES) != 0)) {
224 
225 			bluetoothStack.setLocalDeviceServiceClasses(ServiceRecordsRegistry.getDeviceServiceClasses());
226 
227 			serviceRecord.deviceServiceClassesRegistered = serviceRecord.deviceServiceClasses;
228 		}
229 	}
230 }