/* * $HeadURL$ * * * Copyright (c) 2001-2008 Motorola, Inc. All rights reserved. * * * Licensed 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. * * * Revision History: * * Date Author Comment * --------------------------------------------------------------------------------- * Oct 15,2006 Motorola, Inc. Initial creation * */ package BluetoothTCKAgent; import java.io.*; import javax.bluetooth.*; import javax.microedition.io.*; public class PushBTConnectionThread extends Thread { private String message; private String port; private String data = "data", command = "Command"; private StreamConnectionNotifier server = null; private InputStream in = null; private OutputStream out = null; private StreamConnection channel = null; private boolean can_run = true; int buffersize = 700, counter = 0, ch = -5, timeout = 0; private byte[] buffer; private static final String SUCCESS = "PASSED"; private static final String FAIL = "FAILED"; private static final String MSG_TERMINATOR = "\n"; /** * Default timeout value if not set by the user */ private int configTimeout = TCKAgentUtil.SHORT; public PushBTConnectionThread(String str, String port, final String customTimeout) { super(str); try { this.port = port; server = (StreamConnectionNotifier) Connector.open( "socket://:" + port, Connector.READ_WRITE, true); } catch (Exception e) { e.printStackTrace(); } this.setConfigTimeout(customTimeout); } public void run() { while (can_run) { try { System.out .println("PushBTConnectionThread: Waiting for Client to Connect on port " + port); channel = server.acceptAndOpen(); } catch (Exception e) { System.out .println("PushBTConnectionThread: Error occurred when " + "connecting with client:" + e); can_run = false; command = "CLOSE"; } if (can_run) { System.out.println("PushBTConnectionThread: Client made " + "a Connection"); try { in = channel.openInputStream(); out = channel.openOutputStream(); } catch (Exception e) { System.out .println("PushBTConnectionThread: Opening of " + "InputStream() & OutputStream" + " failed : " + e); command = "CLOSE"; } } else { command = "CLOSE"; } // can_run = true; while (!command.equals("CLOSE")) { buffer = new byte[buffersize]; counter = 0; timeout = 0; try { while ((in.available() == 0) && (timeout < 10)) { TCKAgentUtil.pause(TCKAgentUtil.SHORT); timeout++; } } catch (Exception e) { } if (timeout == 10) { counter = buffersize; } try { // Newlines and carriage returns should end the read // These are all below ASCII 20 while ((ch = in.read()) > 20 && counter < buffersize) { buffer[counter++] = (byte) ch; } } catch (Exception e) { System.out.println("PushBTConnectionThread: Error while" + " reading InputStream " + e); ch = -1; } if (ch == -1 || counter == buffersize || counter == 0) { /* * Implies Connection Timed-Out, Error occurred while * reading, or read buffer got full. */ message = "CLOSE connection"; buffer = message.getBytes(); } message = (new String(buffer)).trim(); int space = message.indexOf(" "); command = message.substring(0, space); data = message.substring(space + 1); if (command.equals("CONNECT")) { System.out .println("PushBTConnectionThread: CONNECT Command Called"); space = data.indexOf(" "); String devId = data.substring(0, space); System.out.println("PushBTConnectionThread: Device Id " + devId); int uuidEnd = data.indexOf(' ', space + 1); String uuid = data.substring(space + 1, uuidEnd); System.out.println("PushBTConnectionThread: UUID " + uuid); int responseRqtEnd = data.indexOf(' ', uuidEnd + 1); String responseRequired = null; String handshake = null; if (responseRqtEnd != -1) { responseRequired = data.substring(uuidEnd + 1, responseRqtEnd); handshake = data.substring(responseRqtEnd); } else { responseRequired = data.substring(uuidEnd + 1); } System.out .println("PushBTConnectionThread: Response Reqt. " + responseRequired); System.out.println("PushBTConnectionThread: Handshake " + handshake); boolean verify = responseRequired .equals("response-required"); TCKAgentUtil.pause(TCKAgentUtil.SHORT); buffer = new byte[buffersize]; try { if (connect(devId, uuid, verify, handshake)) { System.out .println("PushBTConnectionThread: L2CAPConnection Successful"); out.write(SUCCESS.getBytes()); out.write(MSG_TERMINATOR.getBytes()); out.flush(); } else { System.out .println("PushBTConnectionThread: L2CAPConnection Failed"); out.write(FAIL.getBytes()); out.write(MSG_TERMINATOR.getBytes()); out.flush(); } } catch (Exception ne) { ne.printStackTrace(); } } else if (command.equals("CONNECT2URL")) { System.out .println("PushBTConnectionThread: CONNECT2URL Command Called"); space = data.indexOf(" "); String connectionURL = data.substring(0, space); System.out.println("PushBTConnectionThread: ConnectionURL " + connectionURL); int responseRqtEnd = data.indexOf(' ', space + 1); String responseRequired = null; String handshake = null; if (responseRqtEnd != -1) { responseRequired = data.substring(space + 1, responseRqtEnd); handshake = data.substring(responseRqtEnd); } else { responseRequired = data.substring(space + 1); } System.out .println("PushBTConnectionThread: Response Reqt. " + responseRequired); System.out.println("PushBTConnectionThread: Handshake " + handshake); boolean verify = responseRequired .equals("response-required"); TCKAgentUtil.pause(TCKAgentUtil.SHORT); buffer = new byte[buffersize]; try { if (connect(connectionURL, verify, handshake)) { System.out .println("PushBTConnectionThread: L2CAPConnection Successful"); out.write(SUCCESS.getBytes()); out.write(MSG_TERMINATOR.getBytes()); out.flush(); } else { System.out .println("PushBTConnectionThread: L2CAPConnection Failed"); out.write(FAIL.getBytes()); out.write(MSG_TERMINATOR.getBytes()); out.flush(); } } catch (Exception ne) { ne.printStackTrace(); } } else if (command.equals("WAIT")) { try { System.out.println("PushBTConnectionThread: WAIT " + "Command Called"); int timetowait; try { timetowait = Integer.parseInt(data); } catch (NumberFormatException nfe) { timetowait = 10000; // default } System.out.println("PushBTConnectionThread TIME: " + timetowait); Thread.sleep(timetowait); } catch (Exception e) { } } else if (command.equals("GETSRHANDLE")) { System.out.println("PushBTConnectionThread: GETSRHANDLE " + "Command Called"); space = data.indexOf(" "); String devId = data.substring(0, space); System.out.println("PushBTConnectionThread: Device Id " + devId); String uuid = data.substring(space + 1); System.out.println("PushBTConnectionThread: UUID " + uuid); TCKAgentUtil.pause(TCKAgentUtil.SHORT); long handle = getServiceRecordHandle(devId, uuid); String handleStr = Long.toString(handle); try { out.write(handleStr.getBytes()); out.write(MSG_TERMINATOR.getBytes()); out.flush(); } catch (IOException e) { e.printStackTrace(); } } else if (command.equals("GETSDCLASS")) { System.out.println("PushBTConnectionThread: GETSDCLASS " + "Command called."); String address = data.substring(0, 12); TCKAgentUtil.pause(TCKAgentUtil.SHORT); int serviceClasses = TCKAgentUtil.getServiceClass(address, this.configTimeout); String srvClassesStr = Integer.toString(serviceClasses); try { out.write(srvClassesStr.getBytes()); out.write(MSG_TERMINATOR.getBytes()); out.flush(); } catch (IOException e) { e.printStackTrace(); } } else if (command.equals("GETFRNAME")) { System.out.println("PushBTConnectionThread: GETFRNAME " + "Command called."); String address = data.substring(0, 12); TCKAgentUtil.pause(TCKAgentUtil.SHORT); String friendlyName = null; String localDeviceAddress = null; LocalDevice localDevice = null; try { localDevice = LocalDevice.getLocalDevice(); localDeviceAddress = localDevice.getBluetoothAddress(); System.out .println("PushBTConnectionThread.run(): Local Device " + "address : " + localDeviceAddress + " , Address " + "parameter: " + address); } catch (BluetoothStateException bse) { System.out.println("PushBTConnectionThread: Exception " + "while retreiving the friendly name for " + "local device. Ex: " + bse); } // If the address we are looking for is the local device, // then we do not need to do a device inquiry if (localDeviceAddress != null && localDeviceAddress.equalsIgnoreCase(address .trim())) { friendlyName = localDevice.getFriendlyName(); System.out .println("PushBTConnectionThread.run(): Retrieved " + "local device friendly name: " + friendlyName); } else { System.out .println("PushBTConnectionThread.run(): Retreiving " + "friendly name for remote device " + address); RemoteDevice remote = TCKAgentUtil .getRemoteDevice(address); try { friendlyName = remote.getFriendlyName(true); } catch (IOException e) { System.out .println("PushBTConnectionThread: Exception " + "while retrieving the friendly name for " + "device : " + address + ". Ex: " + e); // Return NOT_AVAILABLE in the friendly name to // indicate error } } // This would happen if the Bluetooth subsystem did not // support this feature or if the local device could not // contact the remote device if (friendlyName == null) { friendlyName = "RETRIEVED_NULL_OR_ERROR"; } try { System.out .println("PushBTConnectionThread.run(): Returning " + "friendly name: " + friendlyName); out.write(friendlyName.getBytes()); out.write(MSG_TERMINATOR.getBytes()); out.flush(); } catch (IOException e) { e.printStackTrace(); } } else { // If no command is executed, then CLOSE connectiom if (!command.equals("CLOSE")) { System.out.println("PushBTConnectionThread: ERROR." + "Unrecognized Command"); } } } // While Channel Connection Not Closed System.out .println("PushBTConnectionThread: Closing all Connections" + " to the Client"); try { in.close(); out.close(); channel.close(); } catch (Exception e) { System.out.println("PushBTConnectionThread: Error closing " + "Connections: " + e); } command = "Command"; } // while(can_run) System.out.println("PushBTConnectionThread: Shutting Down Service"); if (server != null) { try { server.close(); } catch (Exception e) { System.out.println("Error closing Server Socket : " + e.getMessage()); } } } // method run() long getServiceRecordHandle(String devId, String uuid) { long handle = -1; UUID uarr[] = { new UUID(uuid, false) }; ServiceRecord records[] = TCKAgentUtil.getServiceRecords(devId, uarr); ServiceRecord record = null; if (records != null && records.length != 0) { record = records[0]; } if (record == null) { System.out .println("DEBUG: PushBTConnectionThread : No records found"); handle = -1; } else { // Get the Service Record Handle (Attribute 0x0000) DataElement value = record.getAttributeValue(0x0000); try { if (value == null) { handle = -1; } else { handle = value.getLong(); } } catch (ClassCastException ce) { // This should not happen handle = -1; // The attribute cannot be represented as a long } } return handle; } boolean connect(String devId, String uuid, boolean verify, String handshake) throws IOException { L2CAPConnection clientChannel = null; UUID uarr[] = { new UUID(uuid, false) }; ServiceRecord records[] = TCKAgentUtil.getServiceRecords(devId, uarr); ServiceRecord record = null; if (records != null && records.length != 0) { record = records[0]; } if (record == null) { System.out .println("DEBUG: PushBTConnectionThread : No records found"); return false; } String url = record.getConnectionURL( ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false); System.out.println("remote url " + url); int bytes_read = -1; byte[] buffer = new byte[512]; String msg = (handshake != null) ? handshake : "HANDSHAKE"; String recvdMsg = ""; String protocol = url.substring(0, url.indexOf(":")); if (!protocol.equals("btl2cap")) { return false; } try { clientChannel = (L2CAPConnection) Connector.open(url); buffer = new byte[clientChannel.getReceiveMTU()]; System.out.println("PushBTConnectionThread: Connected " + "successfully to device"); clientChannel.send(msg.getBytes()); clientChannel.send(MSG_TERMINATOR.getBytes()); System.out.println("PushBTConnectionThread: Sent " + "message \"" + msg + "\" to the device."); if (verify) { System.out.println("PushBTConnectionThread: Waiting for " + "handshake from the device ..."); TCKAgentUtil.pause(TCKAgentUtil.MEDIUM); recvdMsg = ""; // Read the handshake message, terminated by a \n while (recvdMsg.indexOf('\n') == -1) { timeout = 0; while (!clientChannel.ready() && timeout < 10) { TCKAgentUtil.pause(TCKAgentUtil.MEDIUM); timeout++; } if (timeout < 10) { bytes_read = clientChannel.receive(buffer); if (bytes_read > 0) { recvdMsg += new String(buffer); } } else { System.out.println("PushBTConnectionThread: Timed out " + "waiting for handshake ..."); break; } } if (recvdMsg.indexOf('\n') != -1) { return msg.trim().equals( recvdMsg.substring(0, recvdMsg.indexOf('\n')) .trim()); } else { return false; } } else { return true; } } catch (Exception e) { System.out.println("PushBTConnectionThread: Caught an exception " + "while connecting to the URL: " + url); System.out.println("Message: " + msg); System.out.println("Exception : " + e); } finally { try { TCKAgentUtil.pause(TCKAgentUtil.SHORT); clientChannel.close(); } catch (Exception e) { } } return false; } boolean connect(String url, boolean verify, String handshake) { L2CAPConnection clientChannel = null; int bytes_read = -1; byte[] buffer = new byte[512]; String msg = (handshake != null) ? handshake : "HANDSHAKE"; String recvdMsg = ""; String protocol = url.substring(0, url.indexOf(":")); if (!protocol.equals("btl2cap")) { return false; } try { clientChannel = (L2CAPConnection) Connector.open(url); buffer = new byte[clientChannel.getReceiveMTU()]; System.out.println("PushBTConnectionThread: Connected " + "successfully to device"); clientChannel.send(msg.getBytes()); clientChannel.send(MSG_TERMINATOR.getBytes()); System.out.println("PushBTConnectionThread: Sent " + "message \"" + msg + "\" to the device."); if (verify) { System.out.println("PushBTConnectionThread: Waiting for " + "handshake from the device ..."); TCKAgentUtil.pause(TCKAgentUtil.MEDIUM); recvdMsg = ""; // Read the handshake message, terminated by a \n while (recvdMsg.indexOf('\n') == -1) { timeout = 0; while (!clientChannel.ready() && timeout < 10) { TCKAgentUtil.pause(TCKAgentUtil.MEDIUM); timeout++; } if (timeout < 10) { bytes_read = clientChannel.receive(buffer); if (bytes_read > 0) { recvdMsg += new String(buffer); } } else { System.out.println("PushBTConnectionThread: Timed out " + "waiting for handshake ..."); break; } } if (recvdMsg.indexOf('\n') != -1) { return msg.trim().equals( recvdMsg.substring(0, recvdMsg.indexOf('\n')) .trim()); } else { return false; } } else { return true; } } catch (Exception e) { System.out.println("PushBTConnectionThread: Caught an exception " + "while connecting to the URL: " + url); System.out.println("Message: " + msg); System.out.println("Exception : " + e); } finally { try { TCKAgentUtil.pause(TCKAgentUtil.SHORT); clientChannel.close(); } catch (Exception e) { } } return false; } /** * @return the configTimeout */ public int getConfigTimeout() { return configTimeout; } /** * @param configTimeout the configTimeout to set */ private void setConfigTimeout(String customizedTimeout) { if ( customizedTimeout != null ) { configTimeout = Integer.parseInt(customizedTimeout.trim()); System.out.println("Push Use customized timeout sets to: " + configTimeout ); } else { System.out.println("Push Use default timeout: " + configTimeout); } } } // Class PushBTConnectionThread