/* * Copyright © 2013 Nokia Corporation. All rights reserved. * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation. * Oracle and Java are trademarks or registered trademarks of Oracle and/or its * affiliates. Other product and company names mentioned herein may be trademarks * or trade names of their respective owners. * See LICENSE.TXT for license information. */ package com.nokia.example.bcexchanger.comm; import java.io.IOException; import java.util.Enumeration; import java.util.Vector; import javax.bluetooth.DeviceClass; import javax.bluetooth.DiscoveryAgent; import javax.bluetooth.DiscoveryListener; import javax.bluetooth.LocalDevice; import javax.bluetooth.RemoteDevice; import javax.bluetooth.ServiceRecord; import javax.bluetooth.UUID; import javax.obex.Operation; import javax.obex.ResponseCodes; /** * * This class implements service discovery state. In this state an active * service discovery is performed. No OBEX server commands are served. * * This class extends ExchangerState abstract class. This class also implements * DiscoveryListener interface to receive Bluetooth service discovery callbacks. * Service discoveries are run from the separate thread therefore the class * implements Runnable interface * * @see bcexchanger.comm.ExchangerState javax.bluetooth.DiscoveryListener Design * Patterns: State * */ public class ServiceDiscoveryState extends ExchangerState implements DiscoveryListener, Runnable { // Instance variables private Thread serviceDiscoveryThread; private Vector services; private DiscoveryAgent agent; private int serviceDiscoveryID; private Vector devices; private boolean canceled = false; public static int GET = 0; public static int PUT = 1; /** * Constructor * * @param _parent - * the class which is nesting the current state of the state * machine * @param _devices - * a vector of RemoteDevice object representing devices found * during inquiry */ public ServiceDiscoveryState(ExchangerStateParent _parent, Vector _devices, int oper) throws IOException { super(_parent); canceled = false; services = new Vector(); devices = _devices; // initiate Bluetooth LocalDevice local = LocalDevice.getLocalDevice(); agent = local.getDiscoveryAgent(); serviceDiscoveryThread = new Thread(this); serviceDiscoveryThread.start(); } /* * ServiceDiscoveryState does not allow to start any other business card * exchange process. * * @see bcexchanger.comm.ExchangerState#startSending() */ public void startSending(int op) throws Exception { throw new IOException( "Service discovery is in progress. Service discovery has to be canceled before starting new sending process"); } /* * ServiceDiscoveryState allows to cancel discovery process. * * @see bcexchanger.comm.ExchangerState#cancelSending() */ public void cancelSending() { canceled = true; agent.cancelServiceSearch(serviceDiscoveryID); } /* * * Inquiry callbacks are not handled and not supposed to occur, since * inquiry process is not started * * @see javax.bluetooth.DiscoveryListener#deviceDiscovered(javax.bluetooth.RemoteDevice, * javax.bluetooth.DeviceClass) */ public void deviceDiscovered(RemoteDevice arg0, DeviceClass arg1) { throw new RuntimeException( "Internal error #8: ServiceDiscoveryState.deviceDiscovered() should not be called"); } /* * * Inquiry callbacks are not handled and not supposed to occur, since * inquiry process is not started * * @see javax.bluetooth.DiscoveryListener#inquiryCompleted(int) */ public void inquiryCompleted(int arg0) { throw new RuntimeException( "Internal error #9: ServiceDiscoveryState.inquiryCompleted() should not be called"); } public void servicesDiscovered(int id, ServiceRecord[] _services) { for (int i = 0; i < _services.length; i++) { services.addElement(_services[i]); } } public synchronized void serviceSearchCompleted(int arg0, int arg1) { notify(); } /* * (non-Javadoc) * * This methods implements logic of the service discovery process * * @see java.lang.Runnable#run() */ public synchronized void run() { Enumeration e = devices.elements(); // Business card exchanger service UUID UUID[] uuids = new UUID[1]; uuids[0] = new UUID(parent.getUUID(), false); // proceeded with all devices if not canceled while (e.hasMoreElements() && !canceled) { RemoteDevice device = (RemoteDevice) e.nextElement(); try { serviceDiscoveryID = agent.searchServices(null, uuids, device, this); } catch (Exception e0) { // signal error to the MIDlet parent.getListener().onServiceDiscoveryComplete( ExchangerComm.ERROR); parent.setState(new IdleState(parent)); return; } try { wait(); // wait until service search is done on this device } catch (Exception e1) { // Ignore } } if (canceled) { // handle user's cancellation try { parent.getListener().onServiceDiscoveryComplete( ExchangerComm.CANCELED); parent.setState(new IdleState(parent)); } catch (Exception e1) { throw new RuntimeException( "Internal error #10: ServiceDicoveryState.run()"); } } else { // not canceled if (services.isEmpty()) { // no services on devices try { parent.getListener().onServiceDiscoveryComplete( ExchangerComm.NO_RECORDS); parent.setState(new IdleState(parent)); } catch (Exception e1) { } } else if (services.size() == 1) { // one service found, // connect to it try { ServiceRecord serviceRecord = (ServiceRecord) services.firstElement(); parent.getListener().onServiceDiscoveryComplete( ExchangerComm.DONE); SendState state = new SendState(parent, serviceRecord.getConnectionURL( ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false)); parent.setState(state); state.doSend(); state = null; Thread.sleep(new Long(5000).longValue()); ReceiveState rstate = new ReceiveState(parent, serviceRecord.getConnectionURL( ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false)); parent.setState(rstate); rstate.doGet(); rstate = null; Thread.sleep(new Long(5000).longValue()); } catch (Exception e2) { } } else { // several services found, let user choose try { // list of friendly names of devices which contain services Vector friendlyNames = createFriendlyNamesList(services); int index = parent.getListener().resolveMultipleServices( friendlyNames); if (!canceled) { // if not canceled during resolving ServiceRecord serviceRecord = (ServiceRecord) services.elementAt(index); parent.getListener().onServiceDiscoveryComplete( ExchangerComm.DONE); SendState state = new SendState(parent, serviceRecord.getConnectionURL( ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false)); parent.setState(state); state.doSend(); state = null; Thread.sleep(new Long(5000).longValue()); ReceiveState rstate = new ReceiveState(parent, serviceRecord.getConnectionURL( ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false)); parent.setState(rstate); rstate.doGet(); rstate = null; Thread.sleep(new Long(5000).longValue()); } else { // if canceled during resolving parent.getListener().onServiceDiscoveryComplete( ExchangerComm.CANCELED); parent.setState(new IdleState(parent)); } } catch (Exception e1) { } } } } /* * This method creates a list of the friendly names of devices that contain * the servicies * * @param _services - vector of ServiceRecord objects representing found * services @return - vectors of strings containing friendly names of * devices */ private Vector createFriendlyNamesList(Vector _services) { Vector friendlyNames = new Vector(); Enumeration e = _services.elements(); while (e.hasMoreElements()) { ServiceRecord serviceRecord = (ServiceRecord) e.nextElement(); RemoteDevice device = serviceRecord.getHostDevice(); try { friendlyNames.addElement(device.getFriendlyName(false)); } catch (IOException e1) { // If there is an excption getting the friendly name // use the address friendlyNames.addElement(device.getBluetoothAddress()); } } return friendlyNames; } /* * Server OBEX GET command is supported only in IdleState * * @see bcexchanger.comm.ExchangerState#onGet(javax.obex.Operation) */ public int onGet(Operation op) { return ResponseCodes.OBEX_HTTP_CONFLICT; } /* * Server OBEX PUT command is supported only in IdleState * * @see bcexchanger.comm.ExchangerState#onPut(javax.obex.Operation) */ public int onPut(Operation op) { return ResponseCodes.OBEX_HTTP_CONFLICT; } }