/*
* 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;
}
}