/** * BlueCove - Java library for Bluetooth * Copyright (C) 2006-2009 Vlad Skarzhevskyy * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * * @author vlads * @version $Id$ */ package net.sf.bluecove; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; import java.util.Date; import java.util.Timer; import java.util.TimerTask; import javax.bluetooth.DataElement; import javax.bluetooth.LocalDevice; import javax.bluetooth.RemoteDevice; import javax.bluetooth.ServiceRecord; import javax.bluetooth.UUID; import javax.microedition.io.Connection; import javax.microedition.io.Connector; import javax.obex.HeaderSet; import javax.obex.Operation; import javax.obex.ResponseCodes; import javax.obex.ServerRequestHandler; import javax.obex.SessionNotifier; import org.bluecove.tester.log.Logger; import org.bluecove.tester.util.IOUtils; import org.bluecove.tester.util.RuntimeDetect; import org.bluecove.tester.util.StringUtils; import org.bluecove.tester.util.TimeUtils; import net.sf.bluecove.util.BluetoothTypesInfo; public class TestResponderServerOBEX implements Runnable { private SessionNotifier serverConnection; private boolean isStoped = false; private boolean isRunning = false; private Thread thread; private Object threadLocalBluetoothStack; public static final UUID OBEX_OBJECT_PUSH = new UUID(0x1105); public static final String OBEX_OBJECT_PUSH_SERVER_NAME = "OBEX Object Push"; private TestResponderServerOBEX() { } public static TestResponderServerOBEX startServer() { TestResponderServerOBEX srv = new TestResponderServerOBEX(); srv.threadLocalBluetoothStack = Configuration.threadLocalBluetoothStack; srv.thread = RuntimeDetect.cldcStub.createNamedThread(srv, "ServerOBEX"); srv.thread.start(); return srv; } public boolean isRunning() { return isRunning; } public void run() { isStoped = false; boolean deviceServiceClassesUpdated = false; RuntimeDetect.cldcStub.setThreadLocalBluetoothStack(threadLocalBluetoothStack); LocalDevice localDevice; try { localDevice = LocalDevice.getLocalDevice(); if (Configuration.testServerOBEX_TCP.booleanValue()) { serverConnection = (SessionNotifier) Connector .open(BluetoothTypesInfo.PROTOCOL_SCHEME_TCP_OBEX + "://"); } else { StringBuffer url = new StringBuffer(BluetoothTypesInfo.PROTOCOL_SCHEME_BT_OBEX); url.append("://localhost:"); if (Configuration.testServerOBEXObjectPush) { url.append(OBEX_OBJECT_PUSH); url.append(";name="); url.append(OBEX_OBJECT_PUSH_SERVER_NAME); } else { url.append(Configuration.blueCoveOBEXUUID()); url.append(";name="); url.append(Consts.RESPONDER_SERVERNAME); url.append("_ox"); } url.append(Configuration.serverURLParams()); serverConnection = (SessionNotifier) Connector.open(url.toString()); if (Configuration.testServerOBEXObjectPush) { try { ServiceRecord record = localDevice.getRecord(serverConnection); final int OBJECT_TRANSFER_SERVICE = 0x100000; try { record.setDeviceServiceClasses(OBJECT_TRANSFER_SERVICE); } catch (Throwable e) { Logger.debug("setDeviceServiceClasses", e); } DataElement bluetoothProfileDescriptorList = new DataElement(DataElement.DATSEQ); DataElement obbexPushProfileDescriptor = new DataElement(DataElement.DATSEQ); obbexPushProfileDescriptor.addElement(new DataElement(DataElement.UUID, OBEX_OBJECT_PUSH)); obbexPushProfileDescriptor.addElement(new DataElement(DataElement.U_INT_2, 0x100)); bluetoothProfileDescriptorList.addElement(obbexPushProfileDescriptor); record.setAttributeValue(0x0009, bluetoothProfileDescriptorList); final short ATTR_SUPPORTED_FORMAT_LIST_LIST = 0x0303; DataElement supportedFormatList = new DataElement(DataElement.DATSEQ); // any type of object. supportedFormatList.addElement(new DataElement(DataElement.U_INT_1, 0xFF)); record.setAttributeValue(ATTR_SUPPORTED_FORMAT_LIST_LIST, supportedFormatList); final short UUID_PUBLICBROWSE_GROUP = 0x1002; final short ATTR_BROWSE_GRP_LIST = 0x0005; DataElement browseClassIDList = new DataElement(DataElement.DATSEQ); UUID browseClassUUID = new UUID(UUID_PUBLICBROWSE_GROUP); browseClassIDList.addElement(new DataElement(DataElement.UUID, browseClassUUID)); record.setAttributeValue(ATTR_BROWSE_GRP_LIST, browseClassIDList); localDevice.updateRecord(record); } catch (Throwable e) { Logger.error("OBEX Service Updating SDP", e); } } else if (Configuration.testServiceAttributes.booleanValue()) { ServiceRecord record = localDevice.getRecord(serverConnection); if (record == null) { Logger.warn("Bluetooth ServiceRecord is null"); } else { TestResponderServer.buildServiceRecord(record); try { record.setDeviceServiceClasses(BluetoothTypesInfo.DeviceClassConsts.INFORMATION_SERVICE); deviceServiceClassesUpdated = true; } catch (Throwable e) { if (e.getMessage().startsWith("Not Supported on")) { Logger.error("setDeviceServiceClasses " + e.getMessage()); } else { Logger.error("setDeviceServiceClasses", e); } } try { LocalDevice.getLocalDevice().updateRecord(record); Logger.debug("OBEX ServiceRecord updated"); } catch (Throwable e) { Logger.error("OBEX Service Record update error", e); } } } } } catch (Throwable e) { Logger.error("OBEX Server start error", e); isStoped = true; return; } if (deviceServiceClassesUpdated) { Logger.info("DeviceClass:" + BluetoothTypesInfo.toString(localDevice.getDeviceClass())); } try { int errorCount = 0; int count = 0; isRunning = true; boolean showServiceRecordOnce = true; while (!isStoped) { RequestHandler handler = new RequestHandler(); try { count++; Logger.info("Accepting OBEX connections"); if (showServiceRecordOnce) { try { Logger.debug("OxUrl:" + localDevice.getRecord(serverConnection).getConnectionURL( Configuration.getRequiredSecurity(), false)); } catch (IllegalArgumentException e) { Logger.debug("Can't get local serviceRecord", e); } showServiceRecordOnce = false; } if (Configuration.authenticateOBEX.getValue() != 0) { handler.auth = new OBEXTestAuthenticator("server" + count); handler.connectionAccepted(serverConnection.acceptAndOpen(handler, handler.auth)); } else { handler.connectionAccepted(serverConnection.acceptAndOpen(handler)); } } catch (InterruptedIOException e) { isStoped = true; break; } catch (Throwable e) { if (errorCount > 3) { isStoped = true; } if (isStoped) { return; } errorCount++; Logger.error("acceptAndOpen ", e); continue; } errorCount = 0; } } finally { close(); Logger.info("OBEX Server finished! " + TimeUtils.timeNowToString()); isRunning = false; } } void close() { try { if (serverConnection != null) { serverConnection.close(); } Logger.debug("OBEX ServerConnection closed"); } catch (Throwable e) { Logger.error("OBEX Server stop error", e); } } void closeServer() { isStoped = true; close(); } /* * We testing on Java 1.1 and Timer is not important */ private class NoTimeWrapper { Object timer; NoTimeWrapper() { try { timer = new Timer(); } catch (Throwable e) { Logger.warn("OBEX Server has no timer"); } } void schedule(final RequestHandler handler) { if (timer != null) { ((Timer) timer).schedule(new TimerTask() { public void run() { handler.notConnectedClose(); } }, 1000 * 30); } } void cancel() { if (timer != null) { ((Timer) timer).cancel(); } } } private class RequestHandler extends ServerRequestHandler { OBEXTestAuthenticator auth; NoTimeWrapper notConnectedTimer = new NoTimeWrapper(); boolean isConnected = false; Connection cconn; RemoteDevice remoteDevice; void connectionAccepted(Connection cconn) { Logger.info("Received OBEX connection"); this.cconn = cconn; if (!Configuration.testServerOBEX_TCP.booleanValue()) { try { remoteDevice = RemoteDevice.getRemoteDevice(cconn); Logger.debug("connected toBTAddress " + remoteDevice.getBluetoothAddress()); } catch (IOException e) { Logger.error("OBEX Server error", e); } } if (!isConnected) { notConnectedTimer.schedule(this); } } void notConnectedClose() { if (!isConnected) { Logger.debug("OBEX connection timeout"); IOUtils.closeQuietly(cconn); } } private void debugHeaderSet(HeaderSet headers) { if (headers == null) { return; } try { int[] headerIDArray = headers.getHeaderList(); if (headerIDArray == null) { return; } Logger.debug("Headers.length:" + headerIDArray.length); for (int i = 0; i < headerIDArray.length; i++) { int hi = headerIDArray[i]; Object value = headers.getHeader(hi); Logger.debug("h[" + hi + "]=" + value); } } catch (IOException e) { Logger.error("headers", e); } } public int onConnect(HeaderSet request, HeaderSet reply) { isConnected = true; notConnectedTimer.cancel(); Logger.debug("OBEX onConnect"); debugHeaderSet(request); if (Configuration.authenticate.booleanValue()) { if (!remoteDevice.isAuthenticated()) { return ResponseCodes.OBEX_HTTP_FORBIDDEN; } Logger.debug("OBEX connection Authenticated"); } return ResponseCodes.OBEX_HTTP_OK; } public void onDisconnect(HeaderSet request, HeaderSet reply) { Logger.debug("OBEX onDisconnect"); debugHeaderSet(request); } public int onSetPath(HeaderSet request, HeaderSet reply, boolean backup, boolean create) { Logger.debug("OBEX onSetPath"); debugHeaderSet(request); return super.onSetPath(request, reply, backup, create); } public int onDelete(HeaderSet request, HeaderSet reply) { Logger.debug("OBEX onDelete"); debugHeaderSet(request); return super.onDelete(request, reply); } public int onPut(Operation op) { Logger.debug("OBEX onPut"); try { HeaderSet hs = op.getReceivedHeaders(); debugHeaderSet(hs); String name = (String) hs.getHeader(HeaderSet.NAME); if (name != null) { Logger.debug("name:" + name); HeaderSet sendHeaders = createHeaderSet(); sendHeaders.setHeader(HeaderSet.DESCRIPTION, name); op.sendHeaders(sendHeaders); } InputStream is = op.openInputStream(); StringBuffer buf = new StringBuffer(); while (!isStoped) { int data = is.read(); if (data == -1) { Logger.debug("EOS recived"); break; } char c = (char) data; buf.append(c); if ((c == '\n') || (buf.length() > 30)) { Logger.debug("cc:" + StringUtils.toBinaryText(buf)); buf = new StringBuffer(); } } if (buf.length() > 0) { Logger.debug("cc:" + StringUtils.toBinaryText(buf)); } op.close(); return ResponseCodes.OBEX_HTTP_OK; } catch (IOException e) { Logger.error("OBEX Server onPut error", e); return ResponseCodes.OBEX_HTTP_UNAVAILABLE; } finally { Logger.debug("OBEX onPut ends"); } } public int onGet(Operation op) { Logger.debug("OBEX onGet"); String message = "Hello client! now " + new Date().toString(); try { HeaderSet hs = op.getReceivedHeaders(); debugHeaderSet(hs); String name = (String) hs.getHeader(HeaderSet.NAME); if (name != null) { message += "\nYou ask for [" + name + "]"; } byte[] messageBytes = message.getBytes(); if (name != null) { HeaderSet sendHeaders = createHeaderSet(); sendHeaders.setHeader(HeaderSet.DESCRIPTION, name); sendHeaders.setHeader(HeaderSet.LENGTH, new Long(messageBytes.length)); op.sendHeaders(sendHeaders); } OutputStream os = op.openOutputStream(); os.write(messageBytes); os.flush(); os.close(); op.close(); return ResponseCodes.OBEX_HTTP_OK; } catch (IOException e) { Logger.error("OBEX Server onGet error", e); return ResponseCodes.OBEX_HTTP_UNAVAILABLE; } finally { Logger.debug("OBEX onGet ends"); } } public void onAuthenticationFailure(byte[] userName) { Logger.debug("OBEX AuthFailure " + new String(userName)); } } }