org.symbian.tools.mtw.ui/src/org/symbian/tools/mtw/ui/deployment/bluetooth/BluetoothTarget.java
/**
* Copyright (c) 2010 Symbian Foundation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Symbian Foundation - initial contribution.
* Contributors:
* Description:
* Overview:
* Details:
* Platforms/Drives/Compatibility:
* Assumptions/Requirement/Pre-requisites:
* Failures and causes:
*/
package org.symbian.tools.mtw.ui.deployment.bluetooth;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.LinkedList;
import javax.bluetooth.BluetoothConnectionException;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import javax.microedition.io.Connector;
import javax.obex.ClientSession;
import javax.obex.HeaderSet;
import javax.obex.Operation;
import javax.obex.ResponseCodes;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.ui.IMemento;
import org.symbian.tools.mtw.core.MTWCore;
import org.symbian.tools.mtw.core.projects.IMTWProject;
import org.symbian.tools.mtw.core.runtimes.IPackager;
import org.symbian.tools.mtw.ui.deployment.IDeploymentTarget;
public class BluetoothTarget extends PlatformObject implements IDeploymentTarget {
private static final UUID OBEX_OBJECT_PUSH = new UUID(0x1105);
private String serviceURL;
private RemoteDevice device;
protected String[] exceptionCodes = new String[] { "OBEX_HTTP_UNSUPPORTED_TYPE", "OBEX_HTTP_FORBIDDEN" };
private String message = "Deployment was successful. Please follow on-screen instructions to complete application deployment on your device.";
private final String name;
private final BluetoothTargetType provider;
private final Collection<IStatus> statuses = new LinkedList<IStatus>();
public BluetoothTarget(String name, RemoteDevice device, BluetoothTargetType provider) {
this.name = name;
this.device = device;
this.provider = provider;
}
public IStatus deploy(IMTWProject project, IPackager packager, IProgressMonitor monitor)
throws CoreException {
monitor.beginTask(String.format("Deploying application %s to %s", project.getName(), name),
IProgressMonitor.UNKNOWN);
final File application = packager.packageApplication(project, new SubProgressMonitor(monitor, 100));
try {
deployWidget(application, packager.getFileType(project), new SubProgressMonitor(monitor, 10));
} finally {
application.delete();
}
monitor.done();
return new Status(IStatus.OK, MTWCore.PLUGIN_ID, message);
}
private void deployWidget(File inputWidget, String fileType, IProgressMonitor progressMonitor) throws CoreException {
if (device == null) {
provider.discoverTargets(new SubProgressMonitor(progressMonitor, 10));
if (device == null) {
throw new CoreException(new Status(IStatus.ERROR, MTWCore.PLUGIN_ID, String.format(
"Device %s is not available", name)));
}
}
progressMonitor.beginTask("Deploying application", IProgressMonitor.UNKNOWN);
InputStream in = null;
OutputStream os = null;
Operation putOperation = null;
ClientSession clientSession = null;
try {
if (!provider.isBloothToothConnected()) {
String msg = "Bluetooth is either disabled or not present in the system.";
emitStatus(IStatus.ERROR, msg, progressMonitor);
return;
}
String message = MessageFormat.format("Searching for the service for the selected device \"{0}\"",
new Object[] { getName() });
emitStatus(IStatus.OK, message, progressMonitor);
String servicesFound = getServicesFound();
if (servicesFound == null || servicesFound.length() < 1) {
message = MessageFormat.format("Cannot find service to the device \"{0}\"", new Object[] { getName() });
emitStatus(IStatus.ERROR, message, progressMonitor);
return;
}
message = MessageFormat.format("Service for the device \"{0}\" found", new Object[] { getName() });
emitStatus(IStatus.OK, message, progressMonitor);
clientSession = (ClientSession) Connector.open(servicesFound);
HeaderSet hsConnectReply = clientSession.connect(null);
if (hsConnectReply.getResponseCode() != ResponseCodes.OBEX_HTTP_OK) {
emitStatus(IStatus.OK, "Failed to connect to the service", progressMonitor);
}
emitStatus(IStatus.OK, "Deployment Started", progressMonitor);
HeaderSet hsOperation = clientSession.createHeaderSet();
if (progressMonitor.isCanceled()) {
emitStatus(IStatus.CANCEL, "Deployment was canceled", progressMonitor);
return;
}
// Send widget to server
in = new FileInputStream(inputWidget);
message = MessageFormat.format("Deploying file from {0}", new Object[] { inputWidget.getAbsolutePath() });
emitStatus(IStatus.OK, message, progressMonitor);
hsOperation.setHeader(HeaderSet.NAME, inputWidget.getName());
hsOperation.setHeader(HeaderSet.TYPE, fileType);
int size = (int) inputWidget.length();
byte file[] = new byte[size];
hsOperation.setHeader(HeaderSet.LENGTH, new Long(file.length));
// Create PUT Operation
putOperation = clientSession.put(hsOperation);
os = putOperation.openOutputStream();
long start = System.currentTimeMillis();
byte[] buf = new byte[16 * 1024];
int len;
while ((len = in.read(buf)) > 0) {
os.write(buf, 0, len);
if (progressMonitor.isCanceled()) {
putOperation.abort();
emitStatus(IStatus.CANCEL, "Deployment was canceled", progressMonitor);
return;
}
}
os.flush();
os.close();
long elapsed = System.currentTimeMillis() - start;
emitStatus(IStatus.OK, "elapsed time: " + elapsed / 1000.0 + " seconds", progressMonitor);
int responseCode = putOperation.getResponseCode();
if (responseCode == ResponseCodes.OBEX_HTTP_OK) {
message = MessageFormat.format("File deployed to {0}", new Object[] { getName() });
emitStatus(IStatus.OK, message, progressMonitor);
} else {
message = "Error during deployment, OBEX error: " + responseCode;
emitStatus(IStatus.ERROR, message, progressMonitor);
}
} catch (BluetoothConnectionException x) {
String message = getExceptionMessage(x.getMessage());
emitStatus(IStatus.ERROR, message, progressMonitor);
} catch (IOException e) {
String message = getExceptionMessage(e.getMessage());
emitStatus(IStatus.ERROR, message, progressMonitor);
} finally {
try {
if (in != null) {
in.close();
}
if (putOperation != null) {
putOperation.close();
}
if (clientSession != null) {
clientSession.disconnect(null);
clientSession.close();
}
} catch (EOFException eof) {
// EOFException is now caught
// Ignore the error since deployment has already completed
//Activator.log(IStatus.ERROR, "EOF encountered while cleaning up Bluetooth deployment", eof);
} catch (IOException x) {
MTWCore.log("Error cleaning up BlueTooth deployment", x);
}
}
return;
}
protected void emitStatus(int severity, String statusDescription, IProgressMonitor monitor) {
statuses.add(new Status(severity, MTWCore.PLUGIN_ID, statusDescription));
monitor.setTaskName(statusDescription);
if (severity != IStatus.OK) {
message = statusDescription;
}
}
/**
* Returns the customized methods from the exception error code. If it
* matches it returns the customized message else returns the exception itself
* @param message exception message
* @return the customized message
*/
protected String getExceptionMessage(String message) {
if (message.contains(exceptionCodes[0])) {
return "Device does not support the widget deployment";
} else if (message.contains(exceptionCodes[1])) {
return "Deployment rejected by the device";
}
return message;
}
public String getId() {
return getName();
}
public String getName() {
return name;
}
private String getServicesFound() {
try {
serviceURL = "";
UUID serviceUUID = OBEX_OBJECT_PUSH;
final Object serviceSearchCompletedEvent = new Object();
DiscoveryListener listener = new DiscoveryListener() {
public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
}
public void inquiryCompleted(int discType) {
}
public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {
try {
for (int i = 0; i < servRecord.length; i++) {
if (servRecord[i].getHostDevice().getFriendlyName(false).equals(name)) {
serviceURL = servRecord[i].getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT,
false);
}
}
} catch (BluetoothStateException e) {
MTWCore.log(e.getMessage(), e);
} catch (IOException e) {
MTWCore.log(e.getMessage(), e);
}
}
public void serviceSearchCompleted(int transID, int respCode) {
synchronized (serviceSearchCompletedEvent) {
serviceSearchCompletedEvent.notifyAll();
}
}
};
UUID[] searchUuidSet = new UUID[] { serviceUUID };
int[] attrIDs = new int[] { 0x0100 }; // Service name
synchronized (serviceSearchCompletedEvent) {
LocalDevice.getLocalDevice().getDiscoveryAgent()
.searchServices(attrIDs, searchUuidSet, device, listener);
serviceSearchCompletedEvent.wait();
}
} catch (IOException e) {
MTWCore.log("Error in Bluetooth service discovery", e);
} catch (InterruptedException e) {
MTWCore.log("Error in Bluetooth service discovery", e);
}
return serviceURL;
}
public void load(IMemento memento) {
// nothing
}
public void save(IMemento memento) {
// nothing
}
public void setAddress(RemoteDevice device) {
this.device = device;
}
public boolean isDiscovered() {
return device != null;
}
public String getDescription() {
return device == null ? "This device was remembered from past sessions and may not be available" : String
.format("Remote device with address %s", device.getBluetoothAddress());
}
}