Package javax.bluetooth

Source Code of javax.bluetooth.DiscoveryAgent

/*
*  (c) Copyright 2003 Christian Lorenz  ALL RIGHTS RESERVED.
*
* This file is part of the JavaBluetooth Stack.
*
* The JavaBluetooth Stack is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* The JavaBluetooth Stack 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 for more details.
*
*/


package javax.bluetooth;

import java.io.*;
import java.util.*;
import org.javabluetooth.stack.BluetoothStack;
import org.javabluetooth.stack.hci.HCIException;
import org.javabluetooth.stack.sdp.SDPClientChannel;

/**
* The <code>DiscoveryAgent</code> class provides methods to perform
* device and service discovery.  A local device must have only one
* <code>DiscoveryAgent</code> object.  This object must be retrieved by a call to <code>getDiscoveryAgent()</code> on the
* <code>LocalDevice</code> object. <H3>Device Discovery</H3> There are two ways to discover devices.  First, an application
* may use <code>startInquiry()</code> to start an inquiry to find devices
* in proximity to the local device. Discovered devices are returned
* via the <code>deviceDiscovered()</code> method of the interface <code>DiscoveryListener</code>.  The second way to
* discover devices is via the <code>retrieveDevices()</code> method.
* This method will return devices that have been discovered via a
* previous inquiry or devices that are classified as pre-known. (Pre-known devices are those devices that are defined in the
* Bluetooth Control Center as devices this device frequently contacts.)
* The <code>retrieveDevices()</code> method does not perform an
* inquiry, but provides a quick way to get a list of devices that may be in the area. <H3>Service Discovery</H3>
* The <code>DiscoveryAgent</code> class also encapsulates the
* functionality provided by the service discovery application profile.
* The class provides an interface for an application to search and
* retrieve attributes for a particular service.  There are two ways to
* search for services.  To search for a service on a single device,
* the <code>searchServices()</code> method should be used.  On the
* other hand, if you don't care which device a service is on, the
* <code>selectService()</code> method does a service search on a set of remote devices.
* @author Christian Lorenz
*/
public class DiscoveryAgent {
    /** Takes the device out of discoverable mode. <P> The value of <code>NOT_DISCOVERABLE</code> is 0x00 (0). */
    public static final int NOT_DISCOVERABLE = 0;

    /**
     * The inquiry access code for General/Unlimited Inquiry Access Code
     * (GIAC). This is used to specify the type of inquiry to complete or respond to. <P>
     * The value of <code>GIAC</code> is 0x9E8B33 (10390323). This value
     * is defined in the Bluetooth Assigned Numbers document.
     */
    public static final int GIAC = 0x9E8B33;

    /**
     * The inquiry access code for Limited Dedicated Inquiry Access Code
     * (LIAC). This is used to specify the type of inquiry to complete or respond to. <P>
     * The value of <code>LIAC</code> is 0x9E8B00 (10390272). This value
     * is defined in the Bluetooth Assigned Numbers document.
     */
    public static final int LIAC = 0x9E8B00;

    /**
     * Used with the <code>retrieveDevices()</code> method to return
     * those devices that were found via a previous inquiry.  If no
     * inquiries have been started, this will cause the method to return <code>null</code>. <P>
     * The value of <code>CACHED</code> is 0x00 (0).
     * @see #retrieveDevices
     */
    public static final int CACHED = 0x00;

    /**
     * Used with the <code>retrieveDevices()</code> method to return
     * those devices that are defined to be pre-known devices.  Pre-known
     * devices are specified in the BCC.  These are devices that are
     * specified by the user as devices with which the local device will frequently communicate. <P>
     * The value of <code>PREKNOWN</code> is 0x01 (1).
     * @see #retrieveDevices
     */
    public static final int PREKNOWN = 0x01;
    private BluetoothStack bluetoothStack;
    private Vector listeners;
    private Vector cachedRemoteDevices;
    private Vector foundRemoteDevices;
    private Hashtable remoteDevices;
    private boolean isInquiring;
    private int transactionID = 0;

    /**
     * Creates a <code>DiscoveryAgent</code> object.
     *@param bluetoothStack The Instance of the <code>BluetoothStack</code> associated with this <code>DiscoveryAgent</code>.
     * Christian Lorenz: changed constructor to public and added <code>BluetoothStack</code> as a parameter.
     */
    public DiscoveryAgent(BluetoothStack bluetoothStack) {
        this.bluetoothStack = bluetoothStack;
        this.listeners = new Vector();
        this.cachedRemoteDevices = new Vector();
        this.remoteDevices = new Hashtable();
        this.isInquiring = false;
    }

    /*  End of the constructor method   */

    /**
     * Returns an array of Bluetooth devices that have either been found
     * by the local device during previous inquiry requests or been
     * specified as a pre-known device depending on the argument. The list
     * of previously found devices is maintained by the implementation of
     * this API. (In other words, maintenance of the list of previously
     * found devices is an implementation detail.) A device can be set as a pre-known device in the Bluetooth Control Center.
     * @param option <code>CACHED</code> if previously found devices
     * should be returned; <code>PREKNOWN</code> if pre-known devices should be returned
     * @return an array containing the Bluetooth devices that were
     * previously found if <code>option</code> is <code>CACHED</code>; an array of devices that are pre-known devices if
     * <code>option</code> is <code>PREKNOWN</code>; <code>null</code> if no devices meet the criteria
     * Christian Lorenz: This implemenation does not support <code>PREKNOWN</code> devices and will always return
     * <code>null</code>. Use <code>CACHED</code> instead.
     * @exception IllegalArgumentException if <code>option</code> is not <code>CACHED</code> or <code>PREKNOWN</code>
     */
    public RemoteDevice[] retrieveDevices(int option) {
        if (option == PREKNOWN) return null;
        else if (option == CACHED) {
            RemoteDevice[] devices = null;
            synchronized(cachedRemoteDevices) {
                int deviceCount = cachedRemoteDevices.size();
                if (deviceCount > 0) {
                    devices = new RemoteDevice[deviceCount];
                    cachedRemoteDevices.copyInto(devices);
                }
            }
            return devices;
        }
        else throw new IllegalArgumentException("DiscoveryAgent.retrieveDevices: Only CACHED and PREKNOWN are vaild values. ");
    }

    /*  End of the method retrieveDevices   */

    /**
     * Places the device into inquiry mode.  The length of the inquiry is
     * implementation dependent. This method will search for devices with the
     * specified inquiry access code. Devices that responded to the inquiry are returned to the application via the method
     * <code>deviceDiscovered()</code> of the interface <code>DiscoveryListener</code>. The <code>cancelInquiry()</code>
     * method is called to stop the inquiry.
     * @see #cancelInquiry
     * @see #GIAC
     * @see #LIAC
     * @param accessCode  the type of inquiry to complete
     * @param listener the event listener that will receive device discovery events
     * @return <code>true</code> if the inquiry was started; <code>false</code> if the inquiry was not started because the
     * <code>accessCode</code> is not supported
     * @exception IllegalArgumentException if the access code provided
     * is not <code>LIAC</code>, <code>GIAC</code>, or in the range 0x9E8B00 to 0x9E8B3F
     * @exception NullPointerException if <code>listener</code> is <code>null</code>
     * @exception BluetoothStateException if the Bluetooth device does
     * not allow an inquiry to be started due to other operations that are being performed by the device
     */
    public boolean startInquiry(int accessCode, DiscoveryListener listener) throws BluetoothStateException {
        if (listener != null) { if (!listeners.contains(listener)) listeners.addElement(listener); }
        if (isInquiring) {
            Enumeration remoteDevices = foundRemoteDevices.elements();
            while (remoteDevices.hasMoreElements()) {
                RemoteDevice remoteDevice = (RemoteDevice)remoteDevices.nextElement();
                if ((remoteDevice != null) && (listener != null))
                    listener.deviceDiscovered(remoteDevice, remoteDevice.deviceClass);
            }
            return true;
        }
        else {
            isInquiring = true;
            foundRemoteDevices = new Vector();
            bluetoothStack.registerDiscoveryAgent(this);
            try {
                byte result = bluetoothStack.send_HCI_LC_Inquiry(GIAC, 60, (byte)0x00);
                if (result == 0) return true;
                else return false;
            }
            catch (HCIException e) { throw new BluetoothStateException("HCIException: DiscoveryAgent.startInquiry(): " + e); }
        }
    }

    /*  End of the method  startInquiry     */

    /**
     * Removes the device from inquiry mode. <P> An <code>inquiryCompleted()</code> event will occur with a type of
     * <code>INQUIRY_TERMINATED</code> as a result of calling this method.  After receiving this
     * event, no further <code>deviceDiscovered()</code> events will occur as a result of this inquiry. <P>
     * This method will only cancel the inquiry if the <code>listener</code> provided is the listener that started
     * the inquiry. Christian Lorenz: Acctually this implementation will not remove the device
     * from inquiry mode, or cancel any running inquiry. It will only prevent the <code>listener</code> from receiving any
     * further <code>deviceDiscovered()</code> events. This change in behavior is due to the distributed enviroment  which
     * allows multiple <code>DiscoveryAgent</code> to be running on different hosts.
     * @param listener the listener that is receiving inquiry events
     * @return <code>true</code> if the inquiry was canceled; otherwise
     * <code>false</code> if the inquiry was not canceled or if the inquiry was not started using <code>listener</code>
     * Christian Lorenz: This implementation always returns <code>true</code>.
     * @exception NullPointerException if <code>listener</code> is <code>null</code>
     */
    public boolean cancelInquiry(DiscoveryListener listener) {
        if (listener == null) throw new NullPointerException("DiscoveryAgent.cancelInquiry: DiscoveryListener is null.");
        listeners.removeElement(listener);
        listener.inquiryCompleted(DiscoveryListener.INQUIRY_TERMINATED);
        return true;
    }

    /*  End of the method cancelInquiry */

    /**
     * Searches for services on a remote Bluetooth device that have all the
     * UUIDs specified in <code>uuidSet</code>.  Once the service is found,
     * the attributes specified in <code>attrSet</code> and the default attributes are retrieved.  The default attributes are
     * ServiceRecordHandle (0x0000), ServiceClassIDList (0x0001), ServiceRecordState (0x0002), ServiceID (0x0003), and
     * ProtocolDescriptorList (0x0004).If <code>attrSet</code> is
     * <code>null</code> then only the default attributes will be retrieved.
     * <code>attrSet</code> does not have to be sorted in increasing order,
     * but must only contain values in the range [0 - (2<sup>16</sup>-1)].
     * @see DiscoveryListener
     * @param attrSet indicates the attributes whose values will be
     * retrieved on services which have the UUIDs specified in <code>uuidSet</code>
     * @param uuidSet the set of UUIDs that are being searched for;  all
     * services returned will contain all the UUIDs specified here
     * @param btDev the remote Bluetooth device to search for services on
     * @param discListener the object that will receive events when services are discovered
     * @return the transaction ID of the service search; this number must be positive
     * @exception BluetoothStateException if the number of concurrent
     * service search transactions exceeds the limit specified by the
     * <code>bluetooth.sd.trans.max</code> property obtained from the
     * class <code>LocalDevice</code> or the system is unable to start one due to current conditions
     * @exception IllegalArgumentException if <code>attrSet</code> has an illegal service attribute ID or exceeds the property
     * <code>bluetooth.sd.attr.retrievable.max</code> defined in the class <code>LocalDevice</code>; if <code>attrSet</code>
     * or <code>uuidSet</code> is of length 0; if <code>attrSet</code> or <code>uuidSet</code> contains duplicates
     * @exception NullPointerException if <code>uuidSet</code>, <code>btDev</code>, or <code>discListener</code> is
     * <code>null</code>; if an element in  <code>uuidSet</code> array is <code>null</code>
     */
    public int searchServices(int[] attrSet, UUID[] uuidSet, RemoteDevice btDev, DiscoveryListener discListener)
        throws BluetoothStateException {
            int transID = transactionID++;
            DataElement uuidListElement = new DataElement(DataElement.DATSEQ);
            for (int i = 0; i < uuidSet.length; i++) uuidListElement.addElement(new DataElement(DataElement.UUID, uuidSet[i]));
            DataElement attributeListElement = new DataElement(DataElement.DATSEQ);
            for (int i = 0; i < attrSet.length; i++)
                attributeListElement.addElement(new DataElement(DataElement.U_INT_2, attrSet[i]));
            SDPClientChannel sdpChannel = new SDPClientChannel(btDev, discListener);
            try {
                bluetoothStack.connectL2CAPChannel(sdpChannel, btDev, (short)0x0001);
                sdpChannel.send_SDP_ServiceSearchAttributeRequest((short)transID, (short)0xffff, uuidListElement,
                    attributeListElement);
            }
            catch (HCIException e) { throw new BluetoothStateException(e.toString()); }
            catch (IOException e) { throw new BluetoothStateException(e.toString()); }
            return transID;
    }

    /*  End of the method searchServices    */

    /**
     * Cancels the service search transaction that has the specified
     * transaction ID. The ID was assigned to the transaction by the method <code>searchServices()</code>. A
     * <code>serviceSearchCompleted()</code> event with a discovery type
     * of <code>SERVICE_SEARCH_TERMINATED</code> will occur when this method is called. After receiving this event, no further
     * <code>servicesDiscovered()</code> events will occur as a result of this search.
     * @param transID the ID of the service search transaction to cancel; returned by <code>searchServices()</code>
     * @return <code>true</code> if the service search transaction is
     * terminated, else <code>false</code>  if the <code>transID</code>
     * does not represent an active service search transaction Christian Lorenz: This method has no effect. ServiceSearches
     * will continue until they are completed. This method always returns true.
     */
    public boolean cancelServiceSearch(int transID) {
        //TODO cancelSearchServices
        return true;
    }

    /*  End of the method cancelServiceSearch   */

    /**
     * Attempts to locate a service that contains <code>uuid</code> in the ServiceClassIDList of its service record.  This
     * method will return a string that may be used in <code>Connector.open()</code> to establish a connection to the
     * service.  How the service is selected if there are multiple services with <code>uuid</code> and which devices to
     * search is implementation dependent.
     * @see ServiceRecord#NOAUTHENTICATE_NOENCRYPT
     * @see ServiceRecord#AUTHENTICATE_NOENCRYPT
     * @see ServiceRecord#AUTHENTICATE_ENCRYPT
     * @param uuid the UUID to search for in the ServiceClassIDList
     * @param security specifies the security requirements for a connection to this service; must be one of
     * <code>ServiceRecord.NOAUTHENTICATE_NOENCRYPT</code>, <code>ServiceRecord.AUTHENTICATE_NOENCRYPT</code>, or
     * <code>ServiceRecord.AUTHENTICATE_ENCRYPT</code>
     * @param master determines if this client must be the master of the
     * connection; <code>true</code> if the client must be the master;
     * <code>false</code> if the client can be the master or the slave
     * @return the connection string used to connect to the service
     * with a UUID of <code>uuid</code>; or <code>null</code> if no
     * service could be found with a UUID of <code>uuid</code> in the ServiceClassIDList
     * @exception BluetoothStateException if the Bluetooth system cannot
     * start the request due to the current state of the Bluetooth system
     * @exception NullPointerException if <code>uuid</code> is <code>null</code>
     * @exception IllegalArgumentException if <code>security</code> is
     * not <code>ServiceRecord.NOAUTHENTICATE_NOENCRYPT</code>, <code>ServiceRecord.AUTHENTICATE_NOENCRYPT</code>, or
     * <code>ServiceRecord.AUTHENTICATE_ENCRYPT</code> Christian Lorenz: This method is not implemented. Use
     * searchServices instead.
     */
    public String selectService(UUID uuid, int security, boolean master) throws BluetoothStateException {
        //TODO selectService
        throw new RuntimeException("Not Implemented! Used to compile Code");
    }

    /**
     * Christian Lorenz: This method is called from <code>BluetoothStack.receive_HCI_Event_Inquiry_Result(byte[])</code>.
     * @param eventPacket
     */
    public void receive_HCI_Event_Inquiry_Result(byte[] eventPacket) {
        for (int i = 0; i < eventPacket[3]; i++) {
            long bdAddr = (((long)eventPacket[i * 14 + 4]) & 0xff) | (((long)eventPacket[i * 14 + 5]) & 0xff) << 8 |
                (((long)eventPacket[i * 14 + 6]) & 0xff) << 16 | (((long)eventPacket[i * 14 + 7]) & 0xff) << 24 |
                (((long)eventPacket[i * 14 + 8]) & 0xff) << 32 | (((long)eventPacket[i * 14 + 9]) & 0xff) << 40;
            RemoteDevice remoteDevice = (RemoteDevice)remoteDevices.get(new Long(bdAddr));
            if (remoteDevice == null) remoteDevice = new RemoteDevice(bdAddr);
            remoteDevice.pageScanRepMode = eventPacket[i * 14 + 10];
            remoteDevice.pageScanPeriodMode = eventPacket[i * 14 + 11];
            remoteDevice.pageScanMode = eventPacket[i * 14 + 12];
            int classOfDevice = (int)(eventPacket[i * 14 + 13] & 0xff) | ((int)(eventPacket[i * 14 + 14] & 0xff) << 8) |
                ((int)(eventPacket[i * 14 + 15] & 0xff) << 16);
            if (remoteDevice.deviceClass == null) remoteDevice.deviceClass = new DeviceClass(classOfDevice);
            else remoteDevice.deviceClass.record = classOfDevice;
            remoteDevice.clockOffset = (short)((short)(eventPacket[i * 14 + 16] & 0xff) |
                ((short)(eventPacket[i * 14 + 17] & 0xff) << 8));
            remoteDevices.put(new Long(bdAddr), remoteDevice);
            foundRemoteDevices.addElement(remoteDevice);
            Enumeration discoveryListeners = listeners.elements();
            while (discoveryListeners.hasMoreElements()) {
                DiscoveryListener listener = (DiscoveryListener)discoveryListeners.nextElement();
                if (listener != null) listener.deviceDiscovered(remoteDevice, remoteDevice.deviceClass);
            }
        }
    }

    /**
     * Christian Lorenz: This method is called from <code>BluetoothStack.receive_HCI_Event_Inquiry_Complete(byte[])</code>.
     * @param eventPacket
     */
    public void receive_HCI_Event_Inquiry_Complete(byte[] eventPacket) {
        if (eventPacket[3] == 0) {
            synchronized(cachedRemoteDevices) {
                cachedRemoteDevices.removeAllElements();
                cachedRemoteDevices = foundRemoteDevices;
            }
            Enumeration discoveryListeners = listeners.elements();
            isInquiring = false;
            while (discoveryListeners.hasMoreElements()) {
                DiscoveryListener listener = (DiscoveryListener)discoveryListeners.nextElement();
                listener.inquiryCompleted(DiscoveryListener.INQUIRY_COMPLETED);
            }
            listeners.removeAllElements();
        }
    }

    /**
     * Christian Lorenz: This method is called from <code>BluetoothStack.receive_HCI_Event_Inquiry_Complete(byte[])</code>
     * @param eventPacket
     */
    public void receive_HCI_Event_Remote_Name_Request_Complete(byte[] eventPacket) {
        long bdAddr = (((long)eventPacket[4]) & 0xff) | (((long)eventPacket[5]) & 0xff) << 8 |
            (((long)eventPacket[6]) & 0xff) << 16 | (((long)eventPacket[7]) & 0xff) << 24 |
            (((long)eventPacket[8]) & 0xff) << 32 | (((long)eventPacket[9]) & 0xff) << 40;
        RemoteDevice remoteDevice = (RemoteDevice)remoteDevices.get(new Long(bdAddr));
        if (remoteDevice != null) {
            if (eventPacket[3] == 0) {
                int i = 0;
                while (i < 248 && eventPacket[i + 10] != 0x00) { i++; }
                remoteDevice.friendlyName = new String(eventPacket, 10, i);
            }
            else remoteDevice.friendlyName = "Bluetooth Device (" + remoteDevice.getBluetoothAddress() + ")";
        }
    }

    /**
     * Christian Lorenz: Resolves a Bluetooth Address to a RemoteDevice. Add this for use with Connector.
     * @param remoteAddress Bluetooth Device Address as a long.
     * @return the RemoteDevice matching the Bluetooth Address if it has been Discovered. If the Device hasn't been
     * discovered this returns null.
     */
    public RemoteDevice getRemoteDevice(long remoteAddress)
        { return (RemoteDevice)remoteDevices.get(new Long(remoteAddress)); }
}

/*  End of the definition of class DiscoveryAgent */

 
TOP

Related Classes of javax.bluetooth.DiscoveryAgent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.