Programming Java 2 Micro Edition for Symbian OS 2004 (779882), страница 42
Текст из файла (страница 42)
We obtain a LocalDevice instance and use that to geta DiscoveryAgent, with which we start an inquiry using the generaldiscoverable mode:public void startDeviceSearch() {try {agent.startInquiry(DiscoveryAgent.GIAC, this);} catch(BluetoothStateException bse){//handle}}Note that the startInquiry() method is non-blocking.Every time a device is discovered the deviceDiscovered() methodis called. There may be many devices in range and we may not beinterested in them all so we can use the cod value to filter out unwanteddevices (as shown below).public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {// The major device class of 0x600 is an imaging deviceif ((cod.getMajorDeviceClass() == 0x600) {// The minor device class of 0x80 is a printerif ((cod.getMinorDeviceClass() & 0x80) != 0) {// The service class of 0x40000 is a rendering serviceif ((cod.getServiceClasses() & 0x40000) != 0) {remoteDevices.addElement(btDevice);}}}}PROGRAMMING THE BLUETOOTH APIs217The DeviceClass provides an abstraction of the Class of Device/Service(CoD) record, as defined in the Bluetooth specification Assigned Numbers document (https://www.bluetooth.org/foundry/assignnumb/document/baseband).
The CoD record is a 24-bit number, formatted as shownin Figure 4.2.Note that CoD records and the associated service class and deviceclass values are unrelated to service UUIDs. CoDs classify the device(printer, phone, etc.), whereas UUIDs specify the service that may beoffered by a device.The getMajorDevice() method returns the major device classvalue. A device may have only one value for the major device class. Forimaging, the bits are set in the CoD record representing a value of 0x600(see Figure 4.3).A device can have several values for the minor device class. So weuse the printer bit mask to do an AND with the value returned bygetMinorDevice and check the result is non-zero (see Figure 4.4).octet 3octet 2octet 1majorminordevicedevice classclassreserved forservice classespositioningnetworkingrenderingcapturingobject transferaudiotelephonyinformationFigure 4.2 Class of Device/Service Record.1211109800110Figure 4.3 Major device bit values for imaging.218JAVA APIs FOR BLUETOOTH WIRELESS TECHNOLOGY76541xxxFigure 4.4 Minor device class bit mask for a printer.Similarly, the major service class can have multiple values so wecheck that the rendering bit (bit 18) has been set.
A device that has passedthrough all the filters is then added to the remoteDevices Vector.When the inquiry is finished, the inquiryCompleted() methodis called.public void inquiryCompleted(int discType) {if(discType == DiscoveryListener.INQUIRY_COMPLETED) {btUI.displayDevices(remoteDevices);}else{//take appropriate action}}Assuming the inquiry completed successfully, we return our filteredremoteDevices Vector for further processing.4.3.3 Service DiscoveryOnce we have located suitable devices in the vicinity we can search themfor the required service.
The process is similar to that for device inquiry.First we implement a service search DiscoveryListener. This requiressupplying non-trivial implementations for the following methods:public void servicesDiscovered(int transID, ServiceRecord[] servRecord)public void serviceSearchCompleted(int transID, int respCode)The servicesDiscovered() method is invoked by the implementation when the requested services are discovered.
The implementationprovides a transaction ID identifying the request that initiated the servicesearch and an array of ServiceRecords, each identifying a servicediscovered in response to the request.The serviceSearchCompleted() method is invoked by the implementation when a service search is completed or terminates. As well asthe transaction ID, the implementation also supplies a public staticfinal int code with the following values defined in the DiscoveryListener interface:• SERVICE_SEARCH_COMPLETED indicates that the search completednormallyPROGRAMMING THE BLUETOOTH APIs219• SERVICE_SEARCH_TERMINATED means that the service search wasterminated by the application calling the DiscoveryAgent cancelServiceSearch() method• SERVICE_SEARCH_ERROR indicates the service search terminatedwith an error• SERVICE_SEARCH_NO_RECORDS indicates that the remote devicebeing searched holds no service records• SERVICE_SEARCH_NOT_REACHABLE indicates that the remotedevice to be searched is not reachable (not in range anymore).Before we can start a service search we have to get an instance of theLocalDevice and use it to obtain a DiscoveryAgent as before.
Torequest a search we then invoke the following method on the DiscoveryAgent:public int searchServices(int[] attrSet, UUID[] uuidSet, RemoteDevice btDev,DiscoveryListener discListener)We have to pass in the RemoteDevice we wish to search (whichwe have retrieved previously from a device inquiry) and an appropriatelyimplemented DiscoveryListener. We must also pass in an arrayof UUIDs corresponding to those offered by the service required. Notethat for a successful service search the UUIDs contained in the UUID[]argument must match those contained in the ServiceClassIDListattribute of the ServiceRecord in the SDDB running on the remotedevice.
Hence, we must know in advance the UUIDs of the service weare searching for.We also pass in an array corresponding to the attributes we want toretrieve from the ServiceRecord of the service found. If attrSetis null a default list of attributes will be retrieved (ServiceRecordHandle, ServiceClassIDList, ServiceRecordState, ServiceID and ProtocolDescriptorList, with attribute IDs 0x0000to 0x0004).The integer value returned by the searchServices() method,corresponding to the transaction ID, can be used later to cancel the searchif required. Note that searchServices is a non-blocking method.Let’s see how this all fits together in practice.
The ServiceDiscoverer class listed below implements a DiscoveryListener forservice search:import javax.bluetooth.*;import java.io.*;public class ServiceDiscoverer implements DiscoveryListener {private BluetoothUI btUI;private LocalDevice localDevice;220JAVA APIs FOR BLUETOOTH WIRELESS TECHNOLOGYprivate DiscoveryAgent agent;private ServiceRecord[] serviceRecord;public ServiceDiscoverer(BluetoothUI btUI) {this.btUI = btUI;try {localDevice = LocalDevice.getLocalDevice();}catch(BluetoothStateException bse){ //handle }agent = localDevice.getDiscoveryAgent();}public void startServiceSearch(RemoteDevice btDevice) {UUID[] uuidSet = {new UUID("00112233445566778899AABBCCDDEEFF",false)};try {agent.searchServices(null, uuidSet, btDevice, this);}catch(BluetoothStateException bse) { //handle }}public void servicesDiscovered(int transID,ServiceRecord[] servRecord) {serviceRecord = servRecord;}public void serviceSearchCompleted(int transID, int respCode) {String message = null;if(respCode ==DiscoveryListener.SERVICE_SEARCH_DEVICE_NOT_REACHABLE) {message = "Device not reachable";} else if(respCode ==DiscoveryListener.SERVICE_SEARCH_NO_RECORDS) {message = "Service not available";} else if (respCode ==DiscoveryListener.SERVICE_SEARCH_COMPLETED) {message = “Service search completed”;if (serviceRecord[0] != null) {btUI.serviceFound(serviceRecord);}} else if(respCode ==DiscoveryListener.SERVICE_SEARCH_TERMINATED) {message = "Service search terminated";} else if(respCode == DiscoveryListener.SERVICE_SEARCH_ERROR)message = "Service search error";}btUI.setStatus(message);}public void inquiryCompleted(int discType){}public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod){}}Let’s look at the code above in more detail.
First, we obtain aDiscoveryAgent and use it to start a service search, as follows.PROGRAMMING THE BLUETOOTH APIs221public void startServiceSearch(RemoteDevice btDevice) {UUID[] uuidSet = {new UUID("00112233445566778899AABBCCDDEEFF", false)};try {agent.searchServices(null, uuidSet, btDevice, this);} catch(BluetoothStateException bse) { //handle }}We create a long representation of the UUID of the service for whichwe are searching, which in this is example is the single element in ourUUID[] uuidSet.
In this example, the default attribute set of the servicerecord will suffice, so we pass in null for the attrSet argument.If the required service is found, the servicesDiscovered() methodwill be invoked by the implementation and we cache the service recordsfor all services offered by the device that correspond to our requirements.In the case of a search for generic services, such as SPP, there may bemore than one instance offered by the device.public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {serviceRecord = servRecord;}When the service search is complete (whether successful or not)the searchCompleted() method is invoked. We can interrogate therespCode value to ascertain the success of search, as shown below.public void serviceSearchCompleted(int transID, int respCode) {String message = null;if(respCode == DiscoveryListener.SERVICE_SEARCH_DEVICE_NOT_REACHABLE) {message = "Device not reachable";} else if(respCode == DiscoveryListener.SERVICE_SEARCH_NO_RECORDS) {message = "Service not available";} else if (respCode == DiscoveryListener.SERVICE_SEARCH_COMPLETED) {message = “Service search completed”;if (serviceRecord[0] != null) {btUI.serviceFound(serviceRecord);}} else if(respCode == DiscoveryListener.SERVICE_SEARCH_TERMINATED)message = "Service search terminated";} else if(respCode == DiscoveryListener.SERVICE_SEARCH_ERROR) {message = "Service search error";}btUI.setStatus(message);}Assuming we successfully discovered our service, we pass on itsServiceRecord[] to enable a connection to be established with it.222JAVA APIs FOR BLUETOOTH WIRELESS TECHNOLOGY4.3.4 Connecting to a ServiceOnce we have obtained the service record relating to our requiredservice we have everything we need to connect to the service.
Weuse the getConnectionURL() method of the ServiceRecord toobtain a String encapsulating the necessary information (protocol,Bluetooth address of device providing the service, RFCOMM serverchannel identifier, etc.) to connect to the service.public String getConnectionURL(int requiredSecurity, boolean mustBeMaster)The requiredSecurity argument specifies the level of securityfor the connection and can have one of three values defined in ServiceRecord:public static final int NOAUTHENTICATE_NOENCRYPTpublic static final int AUTHENTICATE_NOENCRYPTpublic static final int AUTHENTICATE_ENCRYPTThe mustBeMaster argument indicates whether the local devicemust be master in connections to this service. If false the local deviceis willing to be master or slave in the relationship.