/**
* BlueCove - Java library for Bluetooth
*
* Java docs licensed under the Apache License, Version 2.0
* http://www.apache.org/licenses/LICENSE-2.0
* (c) Copyright 2001, 2002 Motorola, Inc. ALL RIGHTS RESERVED.
*
* 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.
*
* @version $Id$
*/
package javax.bluetooth;
import com.intel.bluetooth.BluetoothStack;
import com.intel.bluetooth.DebugLog;
import com.intel.bluetooth.RemoteDeviceHelper;
import com.intel.bluetooth.SelectServiceHandler;
/**
* 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.
*
*
*/
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;
/**
* Creates a <code>DiscoveryAgent</code> object.
*/
private DiscoveryAgent() {
}
DiscoveryAgent(BluetoothStack bluetoothStack) {
this();
this.bluetoothStack = bluetoothStack;
}
/**
* 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. A device can be
* set as a pre-known device in the Bluetooth Control Center.
*
* While maintenance of the list of previously found devices is an
* implementation detail, it is essential to ensure a consistent user
* experience in what constitutes a list of cached Bluetooth devices. Thus,
* a Bluetooth API implementation MUST read the list of cached devices from
* the native side every time the
* <code>DiscoveryAgent.retrieveDevices()</code> method, if access to the
* native list is available. If the native Bluetooth implementation does not
* maintain the list of cached devices, or if the list is not readily
* available, a Bluetooth API implementation MUST maintain a similar list
* itself and return this list from the
* <code>DiscoveryAgent.retrieveDevices()</code> method when asked for
* CACHED devices. The returned list in either case MUST contain no more
* than one entry for an individual remote device.
*
* @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
*
* @exception IllegalArgumentException
* if <code>option</code> is not <code>CACHED</code> or
* <code>PREKNOWN</code>
*/
public RemoteDevice[] retrieveDevices(int option) {
return RemoteDeviceHelper.implRetrieveDevices(this.bluetoothStack, option);
}
/**
* 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) {
throw new NullPointerException("DiscoveryListener is null");
}
if ((accessCode != LIAC) && (accessCode != GIAC) && ((accessCode < 0x9E8B00) || (accessCode > 0x9E8B3F))) {
throw new IllegalArgumentException("Invalid accessCode " + accessCode);
}
return this.bluetoothStack.startInquiry(accessCode, listener);
}
/**
* 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.
*
* @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>
*
* @exception NullPointerException
* if <code>listener</code> is <code>null</code>
*/
public boolean cancelInquiry(DiscoveryListener listener) {
if (listener == null) {
throw new NullPointerException("DiscoveryListener is null");
}
DebugLog.debug("cancelInquiry");
return this.bluetoothStack.cancelInquiry(listener);
}
/**
* 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 {
if (uuidSet == null) {
throw new NullPointerException("uuidSet is null");
}
if (uuidSet.length == 0) {
// The same as on Motorola, Nokia and SE Phones
throw new IllegalArgumentException("uuidSet is empty");
}
for (int u1 = 0; u1 < uuidSet.length; u1++) {
if (uuidSet[u1] == null) {
throw new NullPointerException("uuidSet[" + u1 + "] is null");
}
for (int u2 = u1 + 1; u2 < uuidSet.length; u2++) {
if (uuidSet[u1].equals(uuidSet[u2])) {
throw new IllegalArgumentException("uuidSet has duplicate values " + uuidSet[u1].toString());
}
}
}
if (btDev == null) {
throw new NullPointerException("RemoteDevice is null");
}
if (discListener == null) {
throw new NullPointerException("DiscoveryListener is null");
}
for (int i = 0; attrSet != null && i < attrSet.length; i++) {
if (attrSet[i] < 0x0000 || attrSet[i] > 0xffff) {
throw new IllegalArgumentException("attrSet[" + i + "] not in range");
}
}
return this.bluetoothStack.searchServices(attrSet, uuidSet, btDev, discListener);
}
/**
* 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
*/
public boolean cancelServiceSearch(int transID) {
DebugLog.debug("cancelServiceSearch", transID);
return this.bluetoothStack.cancelServiceSearch(transID);
}
/**
* 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.
*
* This method MUST return immediately after a suitable service (i.e. a
* service that matches the specified UUID) is found. The Bluetooth inquiry
* or the Bluetooth service discovery MUST NOT continue after a suitable
* service is found. Note that if there are several suitable services in the
* vicinity, different invocations of this method MAY produce different
* results (i.e. return connection strings pointing at different services).
* This is because Bluetooth inquiry and service discovery processes are
* non-deterministic by their nature.
*
* @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>
*/
public String selectService(UUID uuid, int security, boolean master) throws BluetoothStateException {
return (new SelectServiceHandler(this)).selectService(uuid, security, master);
}
}