/* * $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.InterruptedIOException; import javax.bluetooth.DataElement; import javax.bluetooth.L2CAPConnection; import javax.bluetooth.L2CAPConnectionNotifier; import javax.bluetooth.LocalDevice; import javax.bluetooth.ServiceRecord; import javax.bluetooth.ServiceRegistrationException; import javax.bluetooth.UUID; import BluetoothTCKAgent.Connector; public class L2CAPThread extends Thread { /** * Specifies via the configuration the connection string parameter * receiveMTU and transmitMTU for the L2CAP service that the implementation * supports. The value assigned here must equal the value assignment to the * parameter "bluetooth.agent_mtu" configured on the client side in the * global.xml file. */ static final String BLUETOOTH_AGENT_MTU = "bluetooth.agent_mtu"; static final String TIMEOUT = "timeout"; /** * This variable refers to the value the parameters ReceiveMTU and * TransmitMTU have in the L2CAP service that this thread starts up. The * default value is 512 if it is not overriden by the user via * "bluetooth.agent_mtu" when this thread is started. */ private int agentMtu = 512; /** * Default timeout value if not set by the user */ private int configTimeout = TCKAgentUtil.SHORT; public static String message; private String data = "data", command = "Command"; private L2CAPConnectionNotifier server = null; private L2CAPConnection channel = null, helperchannel = null; int buffersize = 680, counter = 1, bytes_read = 0, timeout = 0; byte[] buffer; private boolean can_run = true; private String connString; private LocalDevice localdevice; public L2CAPThread(String str, final String mtu, final String customTimeout) { super(str); this.setAgentMtu(mtu); this.setConfigTimeout(customTimeout); try { System.out.println("L2CAPThread: Starting L2CAP Service using agentMtu: " + agentMtu + " and timeout: " + configTimeout); connString = "btl2cap://localhost:" + "3B9FA89520078C303355AAA694238F07;" + "ReceiveMTU=" + agentMtu + ";TransmitMTU=" + agentMtu; server = (L2CAPConnectionNotifier) Connector.open(connString); //Adding an attribute value to the service record with a DataElement //of type DataElement.URL try{ localdevice = LocalDevice.getLocalDevice(); System.out.println("Local Device bluetooth address is = " + localdevice.getBluetoothAddress()); ServiceRecord srv_record = localdevice.getRecord(server); DataElement docURL = new DataElement(DataElement.URL, "http://www.motorola.com/"); srv_record.setAttributeValue(0x000A, docURL); localdevice.updateRecord(srv_record); DataElement tmp = srv_record.getAttributeValue(0x000A); System.out.println("The URL attribute added is = " + (String)tmp.getValue()); }catch (Exception e){ System.out.println("Error updating the service record in " + "the local device running the agent. " + "WARNING: Tests trying to retreive service record " + " with an attribute value of dataelement type " + "DataElement.URL will fail."); }//attribute value added to the service record } catch (Exception e) { System.out.println("L2CAPThread: Error starting L2CAP" + "service. Aborting service."); can_run = false; } } public void run() { while (can_run) { try { System.out.println("L2CAPThread: " + "Waiting for Client to Connect"); channel = server.acceptAndOpen(); } catch (InterruptedIOException e) { System.out.println("L2CAPThread:TCK Interrupted"); return; } catch (Exception e) { System.out.println("L2CAPThread: Error connecting to " + "client. Aborting connection."); can_run = false; if ("Stack closed".equals(e.getMessage())) { return; } } finally { if (!can_run) { command = "CLOSE"; } else { System.out.println("L2CAPThread: Client made a " + "connection"); } } can_run = true; while (!command.equals("CLOSE")) { buffer = new byte[buffersize]; System.out.println("L2CAPThread: Reading " + "L2CAPConnection Stream"); try { /* * Keep reading until data comes in */ timeout = 0; while (!channel.ready() && timeout < 10) { TCKAgentUtil.pause(1000); timeout++; } if (timeout < 10) { bytes_read = channel.receive(buffer); System.out .println("L2CAPThread.run(): Channel ReceiveMTU: " + channel.getReceiveMTU()); System.out.println("L2CAPThread.run(): Bytes Read: " + bytes_read); } } catch (Exception e) { System.out.println("L2CAPThread: Failure while " + "reading L2CAPConnection."); timeout = 10; } if (timeout == 10) { System.out.println("L2CAPThread: Client Connection " + "Timed Out. Closing connection"); message = "CLOSE connection"; buffer = message.getBytes(); } // allow sending the empty message. Therefore, do not trim the // message as doing so will not be able to parse out the // command message = (new String(buffer)); System.out.println("L2CAPThread.run(): Message \"" + message + "\" and message size: " + message.length()); int space = message.indexOf(" "); if ( space != -1 ) { command = message.substring(0, space); data = message.substring(space + 1); System.out.println("data: " + data + " data len: " + data.length()); // since the buffer was allocated with size more than the // client message sent // trim it to get rid of access before sending to client data = data.trim(); System.out.println(" data size after trim: " + data.length() + " data: " + data); } if (command.equals("ECHO")) { System.out.println("L2CAPThread: ECHO Command Called"); TCKAgentUtil.pause(TCKAgentUtil.SHORT); try { channel.send(data.getBytes()); } catch (Exception e) { System.out.println("L2CAPThread: Error writing " + "to client. Closing connection"); command = "CLOSE"; } } // ECHO Command else if (command.equals("READ")) { System.out.println("L2CAPThread: READ " + "Command Called"); } // READ Command else if (command.equals("LOG")) { System.out.println("L2CAPThread LOG: " + data); } // LOG Command else if (command.equals("WAIT")) { data = data.trim(); try { System.out.println("L2CAPThread: WAIT " + "Command Called"); int timetowait = Integer.parseInt(data); L2CAPThread.sleep(timetowait); } catch (Exception e) { } } // WAIT Command else if (command.equals("CLIENT")) { System.out.println("L2CAPThread: CLIENT Command Called"); command = "CLOSE"; try { TCKAgentUtil.pause(TCKAgentUtil.SHORT); channel.close(); TCKAgentUtil.pause(TCKAgentUtil.MEDIUM); } catch (Exception e) { System.out.println("L2CAPThread: Error closing" + " existing connection."); } data = "btl2cap://" + data + ";authenticate=false;encrypt=false;master=false" + ";ReceiveMTU=" + agentMtu + ";TransmitMTU=" + agentMtu; try { helperchannel = (L2CAPConnection) Connector.open(data); System.out.println("L2CAPThread: Connected " + "successfully to client"); } catch (Exception e) { System.out.println("L2CAPThread: Unable to " + "connect to the client " + "the following connection" + " string: " + data + " with error: " + e); } try { TCKAgentUtil.pause(TCKAgentUtil.SHORT); helperchannel.close(); } catch (Exception e) { } } // CLIENT Command else if (command.equals("GETSDCLASS")) { System.out.println("L2CAPThread: GETSDCLASS " + "Command Called"); int sdClass = -1; String msg = null, btAddress; btAddress = data.substring(0, 12); command = "CLOSE"; try { channel.close(); //https://opensource.motorola.com/sf/discussion/do/listPosts/projects.jsr82/discussion.google_jsr_82_support.topc1845 //TCKAgentUtil.pause(TCKAgentUtil.SHORT); TCKAgentUtil.pause(TCKAgentUtil.MEDIUM); } catch (Exception e) { System.out.println("L2CAPThread: Error closing" + " existing connection."); } sdClass = TCKAgentUtil.getServiceClass(btAddress, this.configTimeout); System.out.println("L2CAPThread: Retrieved the service " + "classes for " + btAddress + " : " + Integer.toString(sdClass, 16)); if (sdClass == -1) { System.out.println("L2CAPThread: Unable to " + "retrieve ServiceDevice" + "Class of the client" + " device: " + btAddress); } msg = Integer.toString(sdClass); data = "btl2cap://" + data + ";authenticate=false;encrypt=false;master=false" + ";ReceiveMTU=" + agentMtu + ";TransmitMTU=" + agentMtu; try { helperchannel = (L2CAPConnection) Connector.open(data); helperchannel.send(msg.getBytes()); TCKAgentUtil.pause(TCKAgentUtil.SHORT); helperchannel.close(); } catch (Exception e) { System.out.println("L2CAPThread: Unable to " + "send data with the " + "connection string: " + data); } } // GETSDCLASS Command else if (command.equals("GETSRHANDLE")) { System.out.println("L2CAPThread: GETSRHANDLE " + "Command Called"); System.out.println("L2CAPThread: WITH DATA " + data); long recordHandle = -1; String msg = null, btAddress; space = data.indexOf(' '); if (space == -1) { System.out.println("L2CAPThread: Missing UUID"); } String url = data.substring(0, space); System.out .println("L2CAPThread.run(): URL \"" + url + "\""); String uuid = data.substring(space + 1); System.out.println("L2CAPThread.run(): UUID \"" + uuid + "\""); btAddress = url.substring(0, 12); System.out.println("L2CAPThread.run(): BTADDRESS: \"" + btAddress + "\""); command = "CLOSE"; try { channel.close(); TCKAgentUtil.pause(TCKAgentUtil.SHORT); } catch (Exception e) { System.out.println("L2CAPThread: Error closing" + " existing connection."); } UUID uuids[] = { new UUID(uuid, false) }; System.out.println("L2CAPThread: Searching for services " + "with uuid \"" + uuid + "\" on device " + btAddress); ServiceRecord records[] = TCKAgentUtil.getServiceRecords( btAddress, uuids); if (records == null || records.length == 0) { System.out .println("L2CAPThread: Unable to retreive " + "Service records for the service with uuid \"" + uuid + "\" ,running on device \"" + btAddress + "\" ."); } else { System.out.println("L2CAPThread: Retreived a service " + "record"); ServiceRecord record = records[0]; DataElement elem = record.getAttributeValue(0x0000); if (elem == null) { System.out.println("L2CAPThread: Missing Record " + "handle for service record."); } else { recordHandle = elem.getLong(); } } msg = Long.toString(recordHandle); System.out.println("L2CAPThread.run(): Retreived handle " + recordHandle); url = "btl2cap://" + url + ";authenticate=false;encrypt=false;master=false" + ";ReceiveMTU=" + agentMtu + ";TransmitMTU=" + agentMtu; try { helperchannel = (L2CAPConnection) Connector.open(url); System.out.println("L2CAPThread.run(): Sending msg " + "\"" + msg + "\""); helperchannel.send(msg.getBytes()); TCKAgentUtil.pause(TCKAgentUtil.SHORT); helperchannel.close(); } catch (Exception e) { System.out.println("L2CAPThread: Unable to " + "send data with the connection string: " + url + ". Exception: " + e); } } // GETSRHANDLE command else { // If no command is executed, then CLOSE connection if (!command.equals("CLOSE")) { System.out.println("L2CAPThread: Unrecognized Command"); } } } // While Channel Connection Not Closed System.out.println("Closing Channel"); if (channel != null) { try { channel.close(); } catch (Exception e) { } } command = "Command"; System.out.println("L2CAPThread: Connection Closed By Client"); } // While true } // method run() /** * @return the agentMtu */ public int getAgentMtu() { return agentMtu; } /** * @param customizedMTU the agentMtu to set */ private void setAgentMtu(String customizedMTU) { if ( customizedMTU != null ) { this.agentMtu = Integer.parseInt(customizedMTU.trim()); System.out.println("Use customized agentMtu sets to: " + this.agentMtu ); } else { System.out.println("Use default agent MTU: " + this.agentMtu); } } /** * @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("Use customized timeout sets to: " + configTimeout ); } else { System.out.println("Use default timeout: " + configTimeout); } } } // class L2CAPThread