import java.lang.*; import java.io.*; import java.util.*; import javax.microedition.io.*; import javax.bluetooth.*; /** * This class shows a simple client application that performs device * and service * discovery and communicates with a print server to show how the Java * API for Bluetooth wireless technology works. */ public class PrintClient implements DiscoveryListener { /** * The DiscoveryAgent for the local Bluetooth device. */ private DiscoveryAgent agent; /** * The max number of service searches that can occur at any one time. */ private int maxServiceSearches = 0; /** * The number of service searches that are presently in progress. */ private int serviceSearchCount; /** * Keeps track of the transaction IDs returned from searchServices. */ private int transactionID[]; /** * The service record to a printer service that can print the message * provided at the command line. */ private ServiceRecord record; /** * Keeps track of the devices found during an inquiry. */ private Vector deviceList; /** * Creates a PrintClient object and prepares the object for device * discovery and service searching. * * @exception BluetoothStateException if the Bluetooth system could not be * initialized */ public PrintClient() throws BluetoothStateException { /* * Retrieve the local Bluetooth device object. */ LocalDevice local = LocalDevice.getLocalDevice(); /* * Retrieve the DiscoveryAgent object that allows us to perform device * and service discovery. */ agent = local.getDiscoveryAgent(); /* * Retrieve the max number of concurrent service searches that can * exist at any one time. */ try { maxServiceSearches = Integer.parseInt( LocalDevice.getProperty("bluetooth.sd.trans.max")); } catch (NumberFormatException e) { System.out.println("General Application Error"); System.out.println("\tNumberFormatException: " + e.getMessage()); } transactionID = new int[maxServiceSearches]; // Initialize the transaction list for (int i = 0; i < maxServiceSearches; i++) { transactionID[i] = -1; } record = null; deviceList = new Vector(); } /** * Adds the transaction table with the transaction ID provided. * * @param trans the transaction ID to add to the table */ private void addToTransactionTable(int trans) { for (int i = 0; i < transactionID.length; i++) { if (transactionID[i] == -1) { transactionID[i] = trans; return; } } } /** * Removes the transaction from the transaction ID table. * * @param trans the transaction ID to delete from the table */ private void removeFromTransactionTable(int trans) { for (int i = 0; i < transactionID.length; i++) { if (transactionID[i] == trans) { transactionID[i] = -1; return; } } } /** * Completes a service search on each remote device in the list until all * devices are searched or until a printer is found that this application * can print to. * * @param devList the list of remote Bluetooth devices to search * * @return true if a printer service is found; otherwise false if * no printer service was found on the devList provided */ private boolean searchServices(RemoteDevice[] devList) { UUID[] searchList = new UUID[2]; /* * Add the UUID for L2CAP to make sure that the service record * found will support L2CAP. This value is defined in the * Bluetooth Assigned Numbers document. */ searchList[0] = new UUID(0x0100); /* * Add the UUID for the printer service that we are going to use to * the list of UUIDs to search for. (a fictional printer service UUID) */ searchList[1] = new UUID("11111111111111111111111111111111", false); /* * Start a search on as many devices as the system can support. */ for (int i = 0; i < devList.length; i++) { System.out.println("Length = " + devList.length); /* * If we found a service record for the printer service, then * we can end the search. */ if (record != null) { System.out.println("Record is not null"); return true; } try { System.out.println("Starting Service Search on " + devList[i].getBluetoothAddress()); int trans = agent.searchServices(null, searchList, devList[i], this); System.out.println("Starting Service Search " + trans); addToTransactionTable(trans); } catch (BluetoothStateException e) { System.out.println("BluetoothStateException: " + e.getMessage()); /* * Failed to start the search on this device, try another * device. */ } /* * Determine if another search can be started. If not, wait for * a service search to end. */ synchronized (this) { serviceSearchCount++; System.out.println("maxServiceSearches = " + maxServiceSearches); System.out.println("serviceSearchCount = " + serviceSearchCount); if (serviceSearchCount == maxServiceSearches) { System.out.println("Waiting"); try { this.wait(); } catch (Exception e) { } } System.out.println("Done Waiting " + serviceSearchCount); } } /* * Wait until all the service searches have completed. */ while (serviceSearchCount > 0) { synchronized (this) { try { this.wait(); } catch (Exception e) { } } } if (record != null) { System.out.println("Record is not null"); return true; } else { System.out.println("Record is null"); return false; } } /** * Finds the first printer that is available to print to. * * @return the service record of the printer that was found; null if no * printer service was found */ public ServiceRecord findPrinter() { /* * If there are any devices that have been found by a recent inquiry, * we don't need to spend the time to complete an inquiry. */ RemoteDevice[] devList = agent.retrieveDevices(DiscoveryAgent.CACHED); if (devList != null) { if (searchServices(devList)) { return record; } } /* * Did not find any printer services from the list of cached devices. * Will try to find a printer service in the list of pre-known * devices. */ devList = agent.retrieveDevices(DiscoveryAgent.PREKNOWN); if (devList != null) { if (searchServices(devList)) { return record; } } /* * Did not find a printer service in the list of pre-known or cached * devices. So start an inquiry to find all devices that could be a * printer and do a search on those devices. */ /* Start an inquiry to find a printer */ try { agent.startInquiry(DiscoveryAgent.GIAC, this); /* * Wait until all the devices are found before trying to start the * service search. */ synchronized (this) { try { this.wait(); } catch (Exception e) { } } } catch (BluetoothStateException e) { System.out.println("Unable to find devices to search"); } if (deviceList.size() > 0) { devList = new RemoteDevice[deviceList.size()]; deviceList.copyInto(devList); if (searchServices(devList)) { return record; } } return null; } /** * This is the main method of this application. It will print out * the message provided to the first printer that it finds. * * @param args[0] the message to send to the printer */ public static void main(String[] args) { PrintClient client = null; /* * Validate the proper number of arguments exist when starting this * application. */ if ((args == null) || (args.length != 1)) { System.out.println("usage: java PrintClient message"); return; } /* * Create a new PrintClient object. */ try { client = new PrintClient(); } catch (BluetoothStateException e) { System.out.println("Failed to start Bluetooth System"); System.out.println("\tBluetoothStateException: " + e.getMessage()); } /* * Find a printer in the local area */ ServiceRecord printerService = client.findPrinter(); if (printerService != null) { /* * Determine if this service will communicate over RFCOMM or * L2CAP by retrieving the connection string. */ String conURL = printerService.getConnectionURL( ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false); int index= conURL.indexOf(':'); String protocol= conURL.substring(0, index); if (protocol.equals("btspp")) { /* * Since this printer service uses RFCOMM, create an RFCOMM * connection and send the data over RFCOMM. */ RFCOMMPrinterClient printer = new RFCOMMPrinterClient(conURL); printer.printJob(args[0]); } else if (protocol.equals("btl2cap")) { /* * Since this service uses L2CAP, create an L2CAP * connection to the service and send the data to the * service over L2CAP. */ L2CAPPrinterClient printer = new L2CAPPrinterClient(conURL); printer.printJob(args[0]); } else { System.out.println("Unsupported Protocol"); } } else { System.out.println("No Printer was found"); } } /** * Called when a device was found during an inquiry. An inquiry * searches for devices that are discoverable. The same device may * be returned multiple times. * * @see DiscoveryAgent#startInquiry * * @param btDevice the device that was found during the inquiry * * @param cod the service classes, major device class, and minor * device class of the remote device being returned * */ public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) { System.out.println("Found device = " + btDevice.getBluetoothAddress()); /* * Since service search takes time and we are already forced to * complete an inquiry, we will not do a service * search on any device that is not an Imaging device. * The device class of 0x600 is Imaging as * defined in the Bluetooth Assigned Numbers document. */ // if (cod.getMajorDeviceClass() == 0x600) { /* * Imaging devices could be a display, camera, scanner, or * printer. If the imaging device is a printer, * then bit 7 should be set from its minor device * class according to the Bluetooth Assigned * Numbers document. */ // if ((cod.getMinorDeviceClass() & 0x80) != 0) { /* * Now we know that it is a printer. Now we will verify that * it has a rendering service on it. A rendering service may * allow us to print. We will have to do a service search to * get more information if a rendering service exists. If this * device has a rendering service then bit 18 will be set in * the major service classes. */ // if ((cod.getServiceClasses() & 0x40000) != 0) { deviceList.addElement(btDevice); // } // } // } } /** * The following method is called when a service search is completed or * was terminated because of an error. Legal status values * include: * SERVICE_SEARCH_COMPLETED, * SERVICE_SEARCH_TERMINATED, * SERVICE_SEARCH_ERROR, * SERVICE_SEARCH_DEVICE_NOT_REACHABLE, and * SERVICE_SEARCH_NO_RECORDS. * * @param transID the transaction ID identifying the request which * initiated the service search * * @param respCode the response code which indicates the * status of the transaction; guaranteed to be one of the * aforementioned only * */ public void serviceSearchCompleted(int transID, int respCode) { System.out.println("serviceSearchCompleted(" + transID + ", " + respCode + ")"); /* * Removes the transaction ID from the transaction table. */ removeFromTransactionTable(transID); serviceSearchCount--; synchronized (this) { this.notifyAll(); } } /** * Called when service(s) are found during a service search. * This method provides the array of services that have been found. * * @param transID the transaction ID of the service search that is * posting the result * * @param service a list of services found during the search request * * @see DiscoveryAgent#searchServices */ public void servicesDiscovered(int transID, ServiceRecord[] servRecord) { /* * If this is the first record found, then store this record * and cancel the remaining searches. */ if (record == null) { System.out.println("FOund a service " + transID); System.out.println("Length of array = " + servRecord.length); if (servRecord[0] == null) { System.out.println("The service record is null"); } record = servRecord[0]; System.out.println("After this"); if (record == null) { System.out.println("THe Seocnd try was null"); } /* * Cancel all the service searches that are presently * being performed. */ for (int i = 0; i < transactionID.length; i++) { if (transactionID[i] != -1) { System.out.println(agent.cancelServiceSearch(transactionID[i])); } } } } /** * Called when a device discovery transaction is * completed. The discType will be * INQUIRY_COMPLETED if the device discovery * transactions ended normally, * INQUIRY_ERROR if the device * discovery transaction failed to complete normally, * INQUIRY_TERMINATED if the device * discovery transaction was canceled by calling * DiscoveryAgent.cancelInquiry(). * * @param discType the type of request that was completed; one of * INQUIRY_COMPLETED, INQUIRY_ERROR * or INQUIRY_TERMINATED */ public void inquiryCompleted(int discType) { synchronized (this) { try { this.notifyAll(); } catch (Exception e) { } } } } /** * The RFCOMMPrinterClient will make a connection using the connection string * provided and send a message to the server to print the data sent. */ class RFCOMMPrinterClient { /** * Keeps the connection string in case the application would like to make * multiple connections to a printer. */ private String serverConnectionString; /** * Creates an RFCOMMPrinterClient that will send print jobs to a printer. * * @param server the connection string used to connect to the server */ RFCOMMPrinterClient(String server) { serverConnectionString = server; } /** * Sends the data to the printer to print. This method will establish a * connection to the server and send the String in bytes to the printer. * This method will send the data in the default encoding scheme used by * the local virtual machine. * * @param data the data to send to the printer * * @return true if the data was printed; false if the data failed to be * printed */ public boolean printJob(String data) { OutputStream os = null; StreamConnection con = null; try { /* * Open the connection to the server */ con =(StreamConnection)Connector.open(serverConnectionString); /* * Sends data to remote device */ os = con.openOutputStream(); os.write(data.getBytes()); /* * Close all resources */ os.close(); con.close(); } catch (IOException e2) { System.out.println("Failed to print data"); System.out.println("IOException: " + e2.getMessage()); return false; } return true; } } /** * The L2CAPPrinterClient will make a connection using the connection string * provided and send a message to the server to print the data sent. */ class L2CAPPrinterClient { /** * Keeps the connection string in case the application would like to make * multiple connections to a printer. */ private String serverConnectionString; /** * Creates an L2CAPPrinterClient object that will allow an application to * send multiple print jobs to a Bluetooth printer. * * @param server the connection string used to connect to the server */ L2CAPPrinterClient(String server) { serverConnectionString = server; } /** * Sends a print job to the server. The print job will print the message * provided. * * @param msg a non-null message to print * * @return true if the message was printed; false if the message was not * printed */ public boolean printJob(String msg) { L2CAPConnection con = null; byte[] data = null; int index = 0; byte[] temp = null; try { /* * Create a connection to the server */ con = (L2CAPConnection)Connector.open(serverConnectionString); /* * Determine the maximum amount of data I can send to the server. */ int MaxOutBufSize = con.getTransmitMTU(); temp = new byte[MaxOutBufSize]; /* * Send as many packets as are needed to send the data */ data = msg.getBytes(); while (index < data.length) { /* * Determine if this is the last packet to send or if there * will be additional packets */ if ((data.length - index) < MaxOutBufSize) { temp = new byte[data.length - index]; System.arraycopy(data, index, temp, 0, data.length - index); } else { temp = new byte[MaxOutBufSize]; System.arraycopy(data, index, temp, 0, MaxOutBufSize); } con.send(temp); index += MaxOutBufSize; } /* * Close the connection to the server */ con.close(); } catch (BluetoothConnectionException e) { System.out.println("Failed to print message"); System.out.println("\tBluetoothConnectionException: " + e.getMessage()); System.out.println("\tStatus: " + e.getStatus()); } catch (IOException e) { System.out.println("Failed to print message"); System.out.println("\tIOException: " + e.getMessage()); return false; } return true; } // End of method printJob. } // End of class L2CAPPrinterClient