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   *  @version $Id: BluetoothStackMicrosoft.java 2638 2008-12-22 15:12:17Z skarzhevskyy $
23   */
24  package com.intel.bluetooth;
25  
26  import java.io.ByteArrayInputStream;
27  import java.io.IOException;
28  import java.io.InterruptedIOException;
29  import java.util.Enumeration;
30  import java.util.Hashtable;
31  import java.util.Vector;
32  
33  import javax.bluetooth.BluetoothConnectionException;
34  import javax.bluetooth.BluetoothStateException;
35  import javax.bluetooth.DataElement;
36  import javax.bluetooth.DeviceClass;
37  import javax.bluetooth.DiscoveryAgent;
38  import javax.bluetooth.DiscoveryListener;
39  import javax.bluetooth.RemoteDevice;
40  import javax.bluetooth.ServiceRecord;
41  import javax.bluetooth.ServiceRegistrationException;
42  import javax.bluetooth.UUID;
43  
44  class BluetoothStackMicrosoft implements BluetoothStack {
45  
46  	private static final int BTH_MODE_POWER_OFF = 1;
47  
48  	private static final int BTH_MODE_CONNECTABLE = 2;
49  
50  	private static final int BTH_MODE_DISCOVERABLE = 3;
51  
52  	private static BluetoothStackMicrosoft singleInstance = null;
53  
54  	private boolean peerInitialized = false;
55  
56  	private boolean windowsCE;
57  
58  	private long localBluetoothAddress = 0;
59  
60  	private DiscoveryListener currentDeviceDiscoveryListener;
61  
62  	private Thread limitedDiscoverableTimer;
63  
64  	// TODO what is the real number for Attributes retrievable ?
65  	private final static int ATTR_RETRIEVABLE_MAX = 256;
66  
67  	private Hashtable deviceDiscoveryDevices;
68  
69  	private static int connectThreadNumber;
70  
71  	private static synchronized int nextConnectThreadNum() {
72  		return connectThreadNumber++;
73  	}
74  
75  	BluetoothStackMicrosoft() {
76  	}
77  
78  	// ---------------------- Library initialization
79  
80  	public String getStackID() {
81  		return BlueCoveImpl.STACK_WINSOCK;
82  	}
83  
84  	/*
85  	 * (non-Javadoc)
86  	 * 
87  	 * @see com.intel.bluetooth.BluetoothStack#isNativeCodeLoaded()
88  	 */
89  	public native boolean isNativeCodeLoaded();
90  
91  	/*
92  	 * (non-Javadoc)
93  	 * 
94  	 * @see com.intel.bluetooth.BluetoothStack#requireNativeLibraries()
95  	 */
96  	public LibraryInformation[] requireNativeLibraries() {
97  		return LibraryInformation.library(BlueCoveImpl.NATIVE_LIB_MS);
98  	}
99  
100 	public String toString() {
101 		return getStackID();
102 	}
103 
104 	public native int getLibraryVersion();
105 
106 	public native int detectBluetoothStack();
107 
108 	public native void enableNativeDebug(Class nativeDebugCallback, boolean on);
109 
110 	private static native int initializationStatus() throws IOException;
111 
112 	private native void uninitialize();
113 
114 	private native boolean isWindowsCE();
115 
116 	public void initialize() throws BluetoothStateException {
117 		if (singleInstance != null) {
118 			throw new BluetoothStateException("Only one instance of " + getStackID() + " stack supported");
119 		}
120 		try {
121 			int status = initializationStatus();
122 			DebugLog.debug("initializationStatus", status);
123 			if (status == 1) {
124 				peerInitialized = true;
125 			}
126 			windowsCE = isWindowsCE();
127 			singleInstance = this;
128 		} catch (BluetoothStateException e) {
129 			throw e;
130 		} catch (IOException e) {
131 			DebugLog.fatal("initialization", e);
132 			throw new BluetoothStateException(e.getMessage());
133 		}
134 	}
135 
136 	public void destroy() {
137 		if (singleInstance != this) {
138 			throw new RuntimeException("Destroy invalid instance");
139 		}
140 		if (peerInitialized) {
141 			peerInitialized = false;
142 			uninitialize();
143 		}
144 		cancelLimitedDiscoverableTimer();
145 		singleInstance = null;
146 	}
147 
148 	private void initialized() throws BluetoothStateException {
149 		if (!peerInitialized) {
150 			throw new BluetoothStateException("Bluetooth system is unavailable");
151 		}
152 	}
153 
154 	/*
155 	 * (non-Javadoc)
156 	 * 
157 	 * @see com.intel.bluetooth.BluetoothStack#getFeatureSet()
158 	 */
159 	public int getFeatureSet() {
160 		return FEATURE_SERVICE_ATTRIBUTES | (windowsCE ? 0 : FEATURE_SET_DEVICE_SERVICE_CLASSES);
161 	}
162 
163 	// ---------------------- LocalDevice
164 
165 	private native int getDeviceClass(long address);
166 
167 	private native void setDiscoverable(boolean on) throws BluetoothStateException;
168 
169 	private native int getBluetoothRadioMode();
170 
171 	private native String getradioname(long address);
172 
173 	private native int getDeviceVersion(long address);
174 
175 	private native int getDeviceManufacturer(long address);
176 
177 	public String getLocalDeviceBluetoothAddress() {
178 		try {
179 			long socket = socket(false, false);
180 			bind(socket);
181 			localBluetoothAddress = getsockaddress(socket);
182 			String address = RemoteDeviceHelper.getBluetoothAddress(localBluetoothAddress);
183 			storesockopt(socket);
184 			close(socket);
185 			return address;
186 		} catch (IOException e) {
187 			DebugLog.error("get local bluetoothAddress", e);
188 			return "000000000000";
189 		}
190 	}
191 
192 	public String getLocalDeviceName() {
193 		if (localBluetoothAddress == 0) {
194 			getLocalDeviceBluetoothAddress();
195 		}
196 		return getradioname(localBluetoothAddress);
197 	}
198 
199 	public String getRemoteDeviceFriendlyName(long address) throws IOException {
200 		return getpeername(address);
201 	}
202 
203 	public DeviceClass getLocalDeviceClass() {
204 		return new DeviceClass(getDeviceClass(localBluetoothAddress));
205 	}
206 
207 	/*
208 	 * (non-Javadoc)
209 	 * 
210 	 * @see com.intel.bluetooth.BluetoothStack#setLocalDeviceServiceClasses(int)
211 	 */
212 	public void setLocalDeviceServiceClasses(int classOfDevice) {
213 		// Done in rfServerUpdateServiceRecord
214 	}
215 
216 	private void cancelLimitedDiscoverableTimer() {
217 		if (limitedDiscoverableTimer != null) {
218 			limitedDiscoverableTimer.interrupt();
219 			limitedDiscoverableTimer = null;
220 		}
221 	}
222 
223 	public boolean setLocalDeviceDiscoverable(int mode) throws BluetoothStateException {
224 		switch (mode) {
225 		case DiscoveryAgent.NOT_DISCOVERABLE:
226 			cancelLimitedDiscoverableTimer();
227 			DebugLog.debug("setDiscoverable(false)");
228 			setDiscoverable(false);
229 			return (DiscoveryAgent.NOT_DISCOVERABLE == getLocalDeviceDiscoverable());
230 		case DiscoveryAgent.GIAC:
231 			cancelLimitedDiscoverableTimer();
232 			DebugLog.debug("setDiscoverable(true)");
233 			setDiscoverable(true);
234 			return (DiscoveryAgent.GIAC == getLocalDeviceDiscoverable());
235 		case DiscoveryAgent.LIAC:
236 			cancelLimitedDiscoverableTimer();
237 			DebugLog.debug("setDiscoverable(LIAC)");
238 			setDiscoverable(true);
239 			if (!(DiscoveryAgent.GIAC == getLocalDeviceDiscoverable())) {
240 				return false;
241 			}
242 			// Timer to turn it off
243 			limitedDiscoverableTimer = Utils.schedule(60 * 1000, new Runnable() {
244 				public void run() {
245 					try {
246 						setDiscoverable(false);
247 					} catch (BluetoothStateException e) {
248 						DebugLog.debug("error setDiscoverable", e);
249 					} finally {
250 						limitedDiscoverableTimer = null;
251 					}
252 				}
253 			});
254 			return true;
255 		}
256 		return false;
257 	}
258 
259 	public boolean isLocalDevicePowerOn() {
260 		int mode = getBluetoothRadioMode();
261 		if (mode == BTH_MODE_POWER_OFF) {
262 			return false;
263 		}
264 		return ((mode == BTH_MODE_CONNECTABLE) || (mode == BTH_MODE_DISCOVERABLE));
265 	}
266 
267 	public int getLocalDeviceDiscoverable() {
268 		int mode = getBluetoothRadioMode();
269 		if (mode == BTH_MODE_DISCOVERABLE) {
270 			if (limitedDiscoverableTimer != null) {
271 				DebugLog.debug("Discoverable = LIAC");
272 				return DiscoveryAgent.LIAC;
273 			} else {
274 				DebugLog.debug("Discoverable = GIAC");
275 				return DiscoveryAgent.GIAC;
276 			}
277 		} else {
278 			DebugLog.debug("Discoverable = NOT_DISCOVERABLE");
279 			return DiscoveryAgent.NOT_DISCOVERABLE;
280 		}
281 	}
282 
283 	public String getLocalDeviceProperty(String property) {
284 		if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_DEVICES_MAX.equals(property)) {
285 			return "7";
286 		}
287 		if (BluetoothConsts.PROPERTY_BLUETOOTH_SD_TRANS_MAX.equals(property)) {
288 			return "7";
289 		}
290 		if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_INQUIRY_SCAN.equals(property)) {
291 			return BlueCoveImpl.TRUE;
292 		}
293 		if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_PAGE_SCAN.equals(property)) {
294 			return BlueCoveImpl.TRUE;
295 		}
296 		if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_INQUIRY.equals(property)) {
297 			return BlueCoveImpl.TRUE;
298 		}
299 		if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_PAGE.equals(property)) {
300 			return BlueCoveImpl.TRUE;
301 		}
302 
303 		if (BluetoothConsts.PROPERTY_BLUETOOTH_SD_ATTR_RETRIEVABLE_MAX.equals(property)) {
304 			return String.valueOf(ATTR_RETRIEVABLE_MAX);
305 		}
306 		if (BluetoothConsts.PROPERTY_BLUETOOTH_MASTER_SWITCH.equals(property)) {
307 			return BlueCoveImpl.FALSE;
308 		}
309 		if (BluetoothConsts.PROPERTY_BLUETOOTH_L2CAP_RECEIVEMTU_MAX.equals(property)) {
310 			return "0";
311 		}
312 
313 		if ("bluecove.radio.version".equals(property)) {
314 			return String.valueOf(getDeviceVersion(localBluetoothAddress));
315 		}
316 		if ("bluecove.radio.manufacturer".equals(property)) {
317 			return String.valueOf(getDeviceManufacturer(localBluetoothAddress));
318 		}
319 		return null;
320 	}
321 
322 	/*
323 	 * (non-Javadoc)
324 	 * 
325 	 * @see com.intel.bluetooth.BluetoothStack#isCurrentThreadInterruptedCallback()
326 	 */
327 	public boolean isCurrentThreadInterruptedCallback() {
328 		return UtilsJavaSE.isCurrentThreadInterrupted();
329 	}
330 
331 	private native boolean retrieveDevicesImpl(int option, RetrieveDevicesCallback retrieveDevicesCallback);
332 
333 	public RemoteDevice[] retrieveDevices(int option) {
334 		if (windowsCE) {
335 			return null;
336 		}
337 		final Vector devices = new Vector();
338 		RetrieveDevicesCallback retrieveDevicesCallback = new RetrieveDevicesCallback() {
339 			public void deviceFoundCallback(long deviceAddr, int deviceClass, String deviceName, boolean paired) {
340 				DebugLog.debug("device found", deviceAddr);
341 				RemoteDevice remoteDevice = RemoteDeviceHelper.createRemoteDevice(BluetoothStackMicrosoft.this,
342 						deviceAddr, deviceName, paired);
343 				devices.add(remoteDevice);
344 			}
345 		};
346 		if (retrieveDevicesImpl(option, retrieveDevicesCallback)) {
347 			return RemoteDeviceHelper.remoteDeviceListToArray(devices);
348 		} else {
349 			return null;
350 		}
351 	}
352 
353 	private native boolean isRemoteDeviceTrustedImpl(long address);
354 
355 	public Boolean isRemoteDeviceTrusted(long address) {
356 		if (windowsCE) {
357 			return null;
358 		}
359 		return new Boolean(isRemoteDeviceTrustedImpl(address));
360 	}
361 
362 	private native boolean isRemoteDeviceAuthenticatedImpl(long address);
363 
364 	public Boolean isRemoteDeviceAuthenticated(long address) {
365 		if (windowsCE) {
366 			return null;
367 		}
368 		return new Boolean(isRemoteDeviceAuthenticatedImpl(address));
369 	}
370 
371 	private native boolean authenticateRemoteDeviceImpl(long address, String passkey) throws IOException;
372 
373 	public boolean authenticateRemoteDevice(long address) throws IOException {
374 		return authenticateRemoteDeviceImpl(address, null);
375 	}
376 
377 	/*
378 	 * (non-Javadoc)
379 	 * 
380 	 * @see com.intel.bluetooth.BluetoothStack#authenticateRemoteDevice(long, java.lang.String)
381 	 */
382 	public boolean authenticateRemoteDevice(long address, String passkey) throws IOException {
383 		return authenticateRemoteDeviceImpl(address, passkey);
384 	}
385 
386 	private native void removeAuthenticationWithRemoteDeviceImpl(long address) throws IOException;
387 
388 	/*
389 	 * (non-Javadoc)
390 	 * 
391 	 * @see com.intel.bluetooth.BluetoothStack#removeAuthenticationWithRemoteDevice (long)
392 	 */
393 	public void removeAuthenticationWithRemoteDevice(long address) throws IOException {
394 		removeAuthenticationWithRemoteDeviceImpl(address);
395 	}
396 
397 	// ---------------------- Device Inquiry
398 
399 	/*
400      * perform synchronous inquiry
401      */
402     private native int runDeviceInquiryImpl(DeviceInquiryRunnable inquiryRunnable, DeviceInquiryThread inquiryThread, int accessCode, int duration,
403             DiscoveryListener listener) throws BluetoothStateException;
404     
405 	public boolean startInquiry(int accessCode, DiscoveryListener listener) throws BluetoothStateException {
406 		initialized();
407 		if (currentDeviceDiscoveryListener != null) {
408 			throw new BluetoothStateException("Another inquiry already running");
409 		}
410 		currentDeviceDiscoveryListener = listener;
411 		
412 		DeviceInquiryRunnable inquiryRunnable = new DeviceInquiryRunnable() {
413 
414             public int runDeviceInquiry(DeviceInquiryThread inquiryThread, int accessCode, DiscoveryListener listener) throws BluetoothStateException {
415                 try {
416                     deviceDiscoveryDevices = new Hashtable();
417                     int discType = runDeviceInquiryImpl(this, inquiryThread, accessCode, DeviceInquiryThread
418                             .getConfigDeviceInquiryDuration(), listener);
419                     if (discType == DiscoveryListener.INQUIRY_COMPLETED) {
420                         for (Enumeration en = deviceDiscoveryDevices.keys(); en.hasMoreElements();) {
421                             RemoteDevice remoteDevice = (RemoteDevice) en.nextElement();
422                             DeviceClass deviceClass = (DeviceClass) deviceDiscoveryDevices.get(remoteDevice);
423                             listener.deviceDiscovered(remoteDevice, deviceClass);
424                             // If cancelInquiry has been called
425                             if (currentDeviceDiscoveryListener == null) {
426                                 return DiscoveryListener.INQUIRY_TERMINATED;
427                             }
428                         }
429                     }
430                     return discType;
431                 } finally {
432                     deviceDiscoveryDevices = null;
433                     currentDeviceDiscoveryListener = null;
434                 }
435             }
436 
437             /**
438              * This is called when all device discoved by stack. To avoid problems with getpeername we will postpone the calls
439              * to User deviceDiscovered function until runDeviceInquiry is finished.
440              */
441             public void deviceDiscoveredCallback(DiscoveryListener listener, long deviceAddr, int deviceClass, String deviceName, boolean paired) {
442                 RemoteDevice remoteDevice = RemoteDeviceHelper.createRemoteDevice(BluetoothStackMicrosoft.this, deviceAddr, deviceName, paired);
443                 if ((currentDeviceDiscoveryListener == null) || (deviceDiscoveryDevices == null)
444                         || (currentDeviceDiscoveryListener != listener)) {
445                     return;
446                 }
447                 DeviceClass cod = new DeviceClass(deviceClass);
448                 DebugLog.debug("deviceDiscoveredCallback address", remoteDevice.getBluetoothAddress());
449                 DebugLog.debug("deviceDiscoveredCallback deviceClass", cod);
450                 deviceDiscoveryDevices.put(remoteDevice, cod);
451                 
452             }
453 		};
454 		return DeviceInquiryThread.startInquiry(this, inquiryRunnable, accessCode, listener);
455 	}
456 
457 	/*
458 	 * cancel current inquiry (if any)
459 	 */
460 	private native boolean cancelInquiry();
461 
462 	public boolean cancelInquiry(DiscoveryListener listener) {
463 		if (currentDeviceDiscoveryListener != listener) {
464 			return false;
465 		}
466 		// no further deviceDiscovered() events will occur for this inquiry
467 		currentDeviceDiscoveryListener = null;
468 		return cancelInquiry();
469 	}
470 
471 	// ---------------------- Service search
472 
473 	/*
474 	 * perform synchronous service discovery
475 	 */
476 	private native int[] runSearchServicesImpl(UUID[] uuidSet, long address) throws SearchServicesException;
477 
478 	/*
479 	 * get service attributes
480 	 */
481 	public native byte[] getServiceAttributes(int[] attrIDs, long address, int handle) throws IOException;
482 
483 	public int searchServices(int[] attrSet, UUID[] uuidSet, RemoteDevice device, DiscoveryListener listener)
484 			throws BluetoothStateException {
485 	    
486 	    SearchServicesRunnable searchRunnable = new SearchServicesRunnable() {
487 
488             public int runSearchServices(SearchServicesThread sst, int[] attrSet, UUID[] uuidSet, RemoteDevice device, DiscoveryListener listener)
489                     throws BluetoothStateException {
490                 sst.searchServicesStartedCallback();
491                 int[] handles;
492                 try {
493                     handles = runSearchServicesImpl(uuidSet, RemoteDeviceHelper.getAddress(device));
494                 } catch (SearchServicesDeviceNotReachableException e) {
495                     return DiscoveryListener.SERVICE_SEARCH_DEVICE_NOT_REACHABLE;
496                 } catch (SearchServicesTerminatedException e) {
497                     return DiscoveryListener.SERVICE_SEARCH_TERMINATED;
498                 } catch (SearchServicesException e) {
499                     return DiscoveryListener.SERVICE_SEARCH_ERROR;
500                 }
501                 if (handles == null) {
502                     return DiscoveryListener.SERVICE_SEARCH_ERROR;
503                 } else if (handles.length > 0) {
504                     ServiceRecord[] records = new ServiceRecordImpl[handles.length];
505                     int[] requiredAttrIDs = new int[] { BluetoothConsts.ServiceRecordHandle,
506                             BluetoothConsts.ServiceClassIDList, BluetoothConsts.ServiceRecordState, BluetoothConsts.ServiceID,
507                             BluetoothConsts.ProtocolDescriptorList };
508                     boolean hasError = false;
509                     for (int i = 0; i < handles.length; i++) {
510                         records[i] = new ServiceRecordImpl(BluetoothStackMicrosoft.this, device, handles[i]);
511                         try {
512                             records[i].populateRecord(requiredAttrIDs);
513                             if (attrSet != null) {
514                                 records[i].populateRecord(attrSet);
515                             }
516                         } catch (Exception e) {
517                             DebugLog.debug("populateRecord error", e);
518                             hasError = true;
519                         }
520                         if (sst.isTerminated()) {
521                             return DiscoveryListener.SERVICE_SEARCH_TERMINATED;
522                         }
523                     }
524                     listener.servicesDiscovered(sst.getTransID(), records);
525                     if (hasError) {
526                         return DiscoveryListener.SERVICE_SEARCH_ERROR;
527                     } else {
528                         return DiscoveryListener.SERVICE_SEARCH_COMPLETED;
529                     }
530                 } else {
531                     return DiscoveryListener.SERVICE_SEARCH_NO_RECORDS;
532                 }
533             }
534 	        
535 	    };
536 		return SearchServicesThread.startSearchServices(this, searchRunnable, attrSet, uuidSet, device, listener);
537 	}
538 
539 	public boolean cancelServiceSearch(int transID) {
540 		SearchServicesThread sst = SearchServicesThread.getServiceSearchThread(transID);
541 		if (sst != null) {
542 			return sst.setTerminated();
543 		} else {
544 			return false;
545 		}
546 	}
547 
548 	public boolean populateServicesRecordAttributeValues(ServiceRecordImpl serviceRecord, int[] attrIDs)
549 			throws IOException {
550 		if (attrIDs.length > ATTR_RETRIEVABLE_MAX) {
551 			throw new IllegalArgumentException();
552 		}
553 		/*
554 		 * retrieve SDP blob
555 		 */
556 		byte[] blob = getServiceAttributes(attrIDs, RemoteDeviceHelper.getAddress(serviceRecord.getHostDevice()),
557 				(int) serviceRecord.getHandle());
558 
559 		if (blob.length > 0) {
560 			try {
561 				boolean anyRetrived = false;
562 				DataElement element = (new SDPInputStream(new ByteArrayInputStream(blob))).readElement();
563 				for (Enumeration e = (Enumeration) element.getValue(); e.hasMoreElements();) {
564 					int attrID = (int) ((DataElement) e.nextElement()).getLong();
565 					serviceRecord.populateAttributeValue(attrID, (DataElement) e.nextElement());
566 					if (!anyRetrived) {
567 						for (int i = 0; i < attrIDs.length; i++) {
568 							if (attrIDs[i] == attrID) {
569 								anyRetrived = true;
570 								break;
571 							}
572 						}
573 					}
574 				}
575 				return anyRetrived;
576 			} catch (IOException e) {
577 				throw e;
578 			} catch (Throwable e) {
579 				throw new IOException();
580 			}
581 		} else {
582 			return false;
583 		}
584 	}
585 
586 	/*
587 	 * socket operations
588 	 */
589 	private native long socket(boolean authenticate, boolean encrypt) throws IOException;
590 
591 	private native long getsockaddress(long socket) throws IOException;
592 
593 	private native void storesockopt(long socket);
594 
595 	private native int getsockchannel(long socket) throws IOException;
596 
597 	private native void connect(long socket, long address, int channel, int retryUnreachable) throws IOException;
598 
599 	private native void bind(long socket) throws IOException;
600 
601 	private native void listen(long socket) throws IOException;
602 
603 	private native long accept(long socket) throws IOException;
604 
605 	private native int recvAvailable(long socket) throws IOException;
606 
607 	private native int recv(long socket) throws IOException;
608 
609 	private native int recv(long socket, byte[] b, int off, int len) throws IOException;
610 
611 	private native void send(long socket, int b) throws IOException;
612 
613 	private native void send(long socket, byte[] b, int off, int len) throws IOException;
614 
615 	private native void close(long socket) throws IOException;
616 
617 	private native String getpeername(long address) throws IOException;
618 
619 	private native long getpeeraddress(long socket) throws IOException;
620 
621 	// ---------------------- Client RFCOMM connections
622 
623 	private class ConnectThread extends Thread {
624 
625 		final Object event;
626 
627 		final long socket;
628 
629 		final BluetoothConnectionParams params;
630 
631 		final int retryUnreachable;
632 
633 		volatile IOException error;
634 
635 		volatile boolean success = false;
636 
637 		volatile boolean connecting = true;
638 
639 		ConnectThread(Object event, long socket, BluetoothConnectionParams params) {
640 			super("ConnectThread-" + nextConnectThreadNum());
641 			this.event = event;
642 			this.socket = socket;
643 			this.params = params;
644 			retryUnreachable = BlueCoveImpl.getConfigProperty(
645 					BlueCoveConfigProperties.PROPERTY_CONNECT_UNREACHABLE_RETRY, 2);
646 
647 		}
648 
649 		public void run() {
650 			try {
651 				connect(socket, params.address, params.channel, retryUnreachable);
652 				success = true;
653 			} catch (IOException e) {
654 				error = e;
655 			} finally {
656 				connecting = false;
657 				synchronized (event) {
658 					event.notifyAll();
659 				}
660 			}
661 		}
662 	}
663 
664 	/*
665 	 * (non-Javadoc)
666 	 * 
667 	 * @see com.intel.bluetooth.BluetoothStack#l2OpenClientConnection(com.intel.bluetooth .BluetoothConnectionParams,
668 	 * int, int)
669 	 */
670 	public long connectionRfOpenClientConnection(final BluetoothConnectionParams params) throws IOException {
671 		final long socket = socket(params.authenticate, params.encrypt);
672 
673 		// Allow to interrupt connection thread
674 		final Object event = new Object();
675 		ConnectThread connectThread = new ConnectThread(event, socket, params);
676 		UtilsJavaSE.threadSetDaemon(connectThread);
677 
678 		boolean timeoutHappend = false;
679 
680 		synchronized (event) {
681 			connectThread.start();
682 			while (connectThread.connecting) {
683 				try {
684 					if (params.timeouts) {
685 						event.wait(params.timeout);
686 						timeoutHappend = connectThread.connecting;
687 						connectThread.interrupt();
688 						break;
689 					} else {
690 						event.wait();
691 					}
692 				} catch (InterruptedException e) {
693 					try {
694 						close(socket);
695 					} catch (Exception ignore) {
696 					}
697 					throw new InterruptedIOException();
698 				}
699 			}
700 		}
701 		if (!connectThread.success) {
702 			try {
703 				close(socket);
704 			} catch (Exception ignore) {
705 			}
706 		}
707 		if (connectThread.error != null) {
708 			throw connectThread.error;
709 		}
710 
711 		if (!connectThread.success) {
712 			if (timeoutHappend) {
713 				throw new BluetoothConnectionException(BluetoothConnectionException.TIMEOUT);
714 			} else {
715 				throw new BluetoothConnectionException(BluetoothConnectionException.FAILED_NOINFO);
716 			}
717 		}
718 		return socket;
719 	}
720 
721 	public void connectionRfCloseClientConnection(long handle) throws IOException {
722 		close(handle);
723 	}
724 
725 	public long rfServerOpen(BluetoothConnectionNotifierParams params, ServiceRecordImpl serviceRecord)
726 			throws IOException {
727 		/*
728 		 * open socket
729 		 */
730 		long socket = socket(params.authenticate, params.encrypt);
731 		boolean success = false;
732 		try {
733 
734 			synchronized (this) {
735 				bind(socket);
736 			}
737 			listen(socket);
738 
739 			int channel = getsockchannel(socket);
740 			DebugLog.debug("service channel ", channel);
741 
742 			long serviceRecordHandle = socket;
743 			serviceRecord.populateRFCOMMAttributes(serviceRecordHandle, channel, params.uuid, params.name, params.obex);
744 
745 			/*
746 			 * register service
747 			 */
748 			serviceRecord.setHandle(registerService(serviceRecord.toByteArray(), serviceRecord.deviceServiceClasses));
749 
750 			success = true;
751 		} finally {
752 			if (!success) {
753 				try {
754 					close(socket);
755 				} catch (IOException e) {
756 					DebugLog.debug("close on failure", e);
757 				}
758 			}
759 		}
760 		return socket;
761 	}
762 
763 	public void rfServerClose(long handle, ServiceRecordImpl serviceRecord) throws IOException {
764 		try {
765 			/*
766 			 * close socket
767 			 */
768 			close(handle);
769 		} finally {
770 			/*
771 			 * unregister service
772 			 */
773 			unregisterService(serviceRecord.getHandle());
774 		}
775 	}
776 
777 	/*
778 	 * register service
779 	 */
780 	private native long registerService(byte[] record, int classOfDevice) throws ServiceRegistrationException;
781 
782 	/*
783 	 * unregister service
784 	 */
785 	private native void unregisterService(long handle) throws ServiceRegistrationException;
786 
787 	public long rfServerAcceptAndOpenRfServerConnection(long handle) throws IOException {
788 		return accept(handle);
789 	}
790 
791 	public void rfServerUpdateServiceRecord(long handle, ServiceRecordImpl serviceRecord, boolean acceptAndOpen)
792 			throws ServiceRegistrationException {
793 		unregisterService(serviceRecord.getHandle());
794 		byte[] blob;
795 		try {
796 			blob = serviceRecord.toByteArray();
797 		} catch (IOException e) {
798 			throw new ServiceRegistrationException(e.toString());
799 		}
800 		serviceRecord.setHandle(registerService(blob, serviceRecord.deviceServiceClasses));
801 		DebugLog.debug("new serviceRecord", serviceRecord);
802 	}
803 
804 	public void connectionRfCloseServerConnection(long handle) throws IOException {
805 		connectionRfCloseClientConnection(handle);
806 	}
807 
808 	public long getConnectionRfRemoteAddress(long handle) throws IOException {
809 		return getpeeraddress(handle);
810 	}
811 
812 	public int connectionRfRead(long handle) throws IOException {
813 		return recv(handle);
814 	}
815 
816 	public int connectionRfRead(long handle, byte[] b, int off, int len) throws IOException {
817 		return recv(handle, b, off, len);
818 	}
819 
820 	public int connectionRfReadAvailable(long handle) throws IOException {
821 		return recvAvailable(handle);
822 	}
823 
824 	public void connectionRfWrite(long handle, int b) throws IOException {
825 		send(handle, b);
826 	}
827 
828 	public void connectionRfWrite(long handle, byte[] b, int off, int len) throws IOException {
829 		send(handle, b, off, len);
830 	}
831 
832 	public void connectionRfFlush(long handle) throws IOException {
833 		// TODO are there any flush
834 	}
835 
836 	public int rfGetSecurityOpt(long handle, int expected) throws IOException {
837 		return expected;
838 	}
839 
840 	/*
841 	 * (non-Javadoc)
842 	 * 
843 	 * @see com.intel.bluetooth.BluetoothStack#l2Encrypt(long,long,boolean)
844 	 */
845 	public boolean rfEncrypt(long address, long handle, boolean on) throws IOException {
846 		return false;
847 	}
848 
849 	// ---------------------- Client and Server L2CAP connections
850 
851 	/*
852 	 * (non-Javadoc)
853 	 * 
854 	 * @see com.intel.bluetooth.BluetoothStack#l2OpenClientConnection(com.intel.bluetooth .BluetoothConnectionParams,
855 	 * int, int)
856 	 */
857 	public long l2OpenClientConnection(BluetoothConnectionParams params, int receiveMTU, int transmitMTU)
858 			throws IOException {
859 		throw new NotSupportedIOException(getStackID());
860 	}
861 
862 	/*
863 	 * (non-Javadoc)
864 	 * 
865 	 * @see com.intel.bluetooth.BluetoothStack#l2CloseClientConnection(long)
866 	 */
867 	public void l2CloseClientConnection(long handle) throws IOException {
868 		throw new NotSupportedIOException(getStackID());
869 	}
870 
871 	/*
872 	 * (non-Javadoc)
873 	 * 
874 	 * @seecom.intel.bluetooth.BluetoothStack#l2ServerOpen(com.intel.bluetooth. BluetoothConnectionNotifierParams, int,
875 	 * int, com.intel.bluetooth.ServiceRecordImpl)
876 	 */
877 	public long l2ServerOpen(BluetoothConnectionNotifierParams params, int receiveMTU, int transmitMTU,
878 			ServiceRecordImpl serviceRecord) throws IOException {
879 		throw new NotSupportedIOException(getStackID());
880 	}
881 
882 	/*
883 	 * (non-Javadoc)
884 	 * 
885 	 * @see com.intel.bluetooth.BluetoothStack#l2ServerUpdateServiceRecord(long, com.intel.bluetooth.ServiceRecordImpl,
886 	 * boolean)
887 	 */
888 	public void l2ServerUpdateServiceRecord(long handle, ServiceRecordImpl serviceRecord, boolean acceptAndOpen)
889 			throws ServiceRegistrationException {
890 		throw new ServiceRegistrationException("Not Supported on" + getStackID());
891 	}
892 
893 	/*
894 	 * (non-Javadoc)
895 	 * 
896 	 * @see com.intel.bluetooth.BluetoothStack#l2ServerAcceptAndOpenServerConnection (long)
897 	 */
898 	public long l2ServerAcceptAndOpenServerConnection(long handle) throws IOException {
899 		throw new NotSupportedIOException(getStackID());
900 	}
901 
902 	/*
903 	 * (non-Javadoc)
904 	 * 
905 	 * @see com.intel.bluetooth.BluetoothStack#l2CloseServerConnection(long)
906 	 */
907 	public void l2CloseServerConnection(long handle) throws IOException {
908 		throw new NotSupportedIOException(getStackID());
909 	}
910 
911 	/*
912 	 * (non-Javadoc)
913 	 * 
914 	 * @see com.intel.bluetooth.BluetoothStack#l2ServerClose(long, com.intel.bluetooth.ServiceRecordImpl)
915 	 */
916 	public void l2ServerClose(long handle, ServiceRecordImpl serviceRecord) throws IOException {
917 		throw new NotSupportedIOException(getStackID());
918 	}
919 
920 	/*
921 	 * (non-Javadoc)
922 	 * 
923 	 * @see com.intel.bluetooth.BluetoothStack#l2GetSecurityOpt(long, int)
924 	 */
925 	public int l2GetSecurityOpt(long handle, int expected) throws IOException {
926 		throw new NotSupportedIOException(getStackID());
927 	}
928 
929 	/*
930 	 * (non-Javadoc)
931 	 * 
932 	 * @see com.intel.bluetooth.BluetoothStack#l2Ready(long)
933 	 */
934 	public boolean l2Ready(long handle) throws IOException {
935 		throw new NotSupportedIOException(getStackID());
936 	}
937 
938 	/*
939 	 * (non-Javadoc)
940 	 * 
941 	 * @see com.intel.bluetooth.BluetoothStack#l2receive(long, byte[])
942 	 */
943 	public int l2Receive(long handle, byte[] inBuf) throws IOException {
944 		throw new NotSupportedIOException(getStackID());
945 	}
946 
947 	/*
948 	 * (non-Javadoc)
949 	 * 
950 	 * @see com.intel.bluetooth.BluetoothStack#l2send(long, byte[])
951 	 */
952 	public void l2Send(long handle, byte[] data) throws IOException {
953 		throw new NotSupportedIOException(getStackID());
954 	}
955 
956 	/*
957 	 * (non-Javadoc)
958 	 * 
959 	 * @see com.intel.bluetooth.BluetoothStack#l2GetReceiveMTU(long)
960 	 */
961 	public int l2GetReceiveMTU(long handle) throws IOException {
962 		throw new NotSupportedIOException(getStackID());
963 	}
964 
965 	/*
966 	 * (non-Javadoc)
967 	 * 
968 	 * @see com.intel.bluetooth.BluetoothStack#l2GetTransmitMTU(long)
969 	 */
970 	public int l2GetTransmitMTU(long handle) throws IOException {
971 		throw new NotSupportedIOException(getStackID());
972 	}
973 
974 	/*
975 	 * (non-Javadoc)
976 	 * 
977 	 * @see com.intel.bluetooth.BluetoothStack#l2RemoteAddress(long)
978 	 */
979 	public long l2RemoteAddress(long handle) throws IOException {
980 		throw new NotSupportedIOException(getStackID());
981 	}
982 
983 	/*
984 	 * (non-Javadoc)
985 	 * 
986 	 * @see com.intel.bluetooth.BluetoothStack#l2Encrypt(long,long,boolean)
987 	 */
988 	public boolean l2Encrypt(long address, long handle, boolean on) throws IOException {
989 		throw new NotSupportedIOException(getStackID());
990 	}
991 }