ServiceDiscoveryState.java
/*
* Copyright © 2012 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 select
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;
}
}