/* BluetoothJokesMIDlet.java Copyright (c) Kari Laitinen http://www.naturalprogramming.com/ 2008-01-25 File created. 2008-01-26 GIAC used instead of LIAC. 2008-02-06 Last modification. This program is a Java midlet that shows the basics of Bluetooth communications between two phones. This application is not a complete joke although it is related to jokes. You can run this program either as a Jokes Server or Jokes Client. The server is able to send a joke to the client whenever the client requests one. The jokes that are sent are simple short texts. WARNING: This program contains some test jokes. I could not resist the temptation to include one joke related to the Swedish people. Despite this joke, I would like to emphasize that I respect Sweden as a nation, and I even like the Swedish language. */ import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import java.io.IOException; import java.util.Vector ; // a Vector is a growable array of objects import java.util.Random ; import javax.bluetooth.BluetoothStateException; import javax.bluetooth.DeviceClass; import javax.bluetooth.DiscoveryAgent; import javax.bluetooth.DiscoveryListener; import javax.bluetooth.L2CAPConnection; import javax.bluetooth.L2CAPConnectionNotifier; import javax.bluetooth.LocalDevice; import javax.bluetooth.RemoteDevice; import javax.bluetooth.ServiceRecord; import javax.bluetooth.UUID; import javax.microedition.io.Connector; class BluetoothJokesServer implements Runnable { private BluetoothJokesMIDlet host_midlet ; private Thread thread_to_run_jokes_server ; private boolean server_thread_should_be_run = true ; private LocalDevice local_device ; private String local_device_name ; private L2CAPConnection connection_to_client ; private String[] jokes_to_be_sent = { "\nQuestion: How many Californians are needed to change a lightbulb?" + "\nAnswer: Six. One to change the lightbulb and five to share the" + " experience", // Ted Nelson from California once told this joke. "\nSon asks: Daddy, why are we going to Australia?" + "\nThe father answers: Stop talking and keep swimming.", "\nQuestion: How many Swedes are needed to change a lightbulb?" + "\nAnswer: None. Swedes do not know that lightbulbs can be changed.", // Swedes and Finns have a love/hate relationship of neighbors. "\nQuestion: Where do you find a one legged dog?" + "\nAnswer: Where you left it.", "\nQuestion: Why did the blonde buy a brown cow?" + "\nAnswer: To get chocolate milk.", "\nQestion: What does Tarzan say when he sees a herd of" + " elephants with sunglasses?" + "\nAnswer: Nothing. He doesn't recognize them." } ; public BluetoothJokesServer( BluetoothJokesMIDlet given_midlet ) { host_midlet = given_midlet; thread_to_run_jokes_server = new Thread( this ) ; thread_to_run_jokes_server.start() ; } public void stop_jokes_server() { if ( thread_to_run_jokes_server != null ) { thread_to_run_jokes_server.interrupt() ; } server_thread_should_be_run = false ; } public void run() { host_midlet.add_to_textbox( "Starting server..." ) ; try { local_device = LocalDevice.getLocalDevice() ; local_device.setDiscoverable( DiscoveryAgent.GIAC ) ; String service_UUID = "00000000000010008000007068039B07"; local_device_name = local_device.getFriendlyName(); String url = "btl2cap://localhost:" + service_UUID + ";name=" + local_device_name; L2CAPConnectionNotifier L2CAP_connection_notifier = (L2CAPConnectionNotifier)Connector.open( url ) ; host_midlet.add_to_textbox( "\nWaiting client ..." ) ; connection_to_client = L2CAP_connection_notifier.acceptAndOpen() ; while ( server_thread_should_be_run == true ) { // When the L2CAPConnection method ready() returns true, // the subsequent call to the receive() method will not // block this thread. if ( connection_to_client.ready() ) { byte[] received_bytes = new byte[ 1000 ] ; int number_of_received_bytes = connection_to_client.receive( received_bytes ) ; String received_data_as_string = new String( received_bytes, 0, number_of_received_bytes ) ; host_midlet.add_to_textbox( "\nFROM CLIENT: " + received_data_as_string ) ; Random random_number_generator = new Random() ; int random_joke_index = random_number_generator.nextInt( jokes_to_be_sent.length ) ; send_to_client( jokes_to_be_sent[ random_joke_index ] ) ; } try { Thread.sleep( 500 ) ; } catch ( InterruptedException caught_interrupted_exception ) { } // System.out.print( " S" ) ; } } catch( BluetoothStateException caught_bluetooth_state_exception ) { System.out.println( caught_bluetooth_state_exception ) ; } catch( IOException caught_ioexception ) { System.out.println( caught_ioexception ); } host_midlet.add_to_textbox( "\nServer thread terminated." ) ; } private void send_to_client( String string_to_be_sent ) { byte[] bytes_to_send = string_to_be_sent.getBytes() ; try { connection_to_client.send( bytes_to_send ) ; } catch( IOException caught_ioexception ) { System.out.println( caught_ioexception ) ; } } } class BluetoothJokesClient implements Runnable, DiscoveryListener { private BluetoothJokesMIDlet host_midlet; private Vector found_bluetooth_devices = new Vector() ; // The ServiceRecord interface describes characteristics // of a Bluetooth service. private ServiceRecord bluetooth_service_record ; private boolean client_thread_should_be_run = true; private String local_device_name; private L2CAPConnection connection_to_server ; boolean jokes_service_discovered = false ; private boolean joke_requesting_enabled = false ; public BluetoothJokesClient( BluetoothJokesMIDlet given_midlet ) { host_midlet = given_midlet; Thread thread_to_run_jokes_client = new Thread( this ) ; thread_to_run_jokes_client.start(); } // Called when a device is found during an inquiry. // An inquiry searches for devices that are discoverable. // The same device may be returned multiple times. public void deviceDiscovered( RemoteDevice discovered_bluetooth_device, DeviceClass class_of_device ) { if( ! found_bluetooth_devices.contains( discovered_bluetooth_device ) ) { found_bluetooth_devices.addElement( discovered_bluetooth_device ) ; try { host_midlet.add_to_textbox( "\nFOUND: " + discovered_bluetooth_device.getFriendlyName( false ) ) ; } catch ( IOException caught_ioexception ) { host_midlet.add_to_textbox( "\n Found unidentified remote device ???!!!" ) ; } } } public void inquiryCompleted( int discovery_type ) { synchronized( this ) { this.notify() ; } } // Called when service(s) are found during a service search public void servicesDiscovered( int transaction_ID_of_service_search, ServiceRecord[] list_of_services_found ) { bluetooth_service_record = list_of_services_found[ 0 ] ; jokes_service_discovered = true ; host_midlet.add_to_textbox( "\nJokes Service Discovered." ) ; } public void serviceSearchCompleted( int transaction_ID_of_service_search, int response_code ) { synchronized( this ) { this.notify() ; } } public void run() { host_midlet.add_to_textbox( "Starting client..." ) ; jokes_service_discovered = false ; try { LocalDevice local_device = LocalDevice.getLocalDevice() ; DiscoveryAgent discovery_agent = local_device.getDiscoveryAgent() ; local_device.setDiscoverable( DiscoveryAgent.GIAC ) ; synchronized( this ) { discovery_agent.startInquiry( DiscoveryAgent.GIAC, this ) ; try { wait() ; } catch ( InterruptedException e ) { } } if ( found_bluetooth_devices.size() == 0 ) { host_midlet.add_to_textbox( "\nNo Bluetooth devices discovered." ) ; } UUID[] set_of_UUIDs_that_will_be_searched_for = { new UUID( "00000000000010008000007068039B07", false ) }; int set_of_attributes[] = { 0x0100 }; int bluetooth_device_index = 0 ; while( jokes_service_discovered == false && bluetooth_device_index < found_bluetooth_devices.size() ) { synchronized( this ) { RemoteDevice bluetooth_device_on_list = (RemoteDevice) found_bluetooth_devices.elementAt( bluetooth_device_index ) ; discovery_agent.searchServices( set_of_attributes, set_of_UUIDs_that_will_be_searched_for, bluetooth_device_on_list, this ) ; try { wait() ; } catch ( InterruptedException e ) { } } bluetooth_device_index ++ ; } } catch ( BluetoothStateException caught_bluetooth_state_exception ) { System.out.println( caught_bluetooth_state_exception ) ; } if ( jokes_service_discovered == true ) { // A Joke Server was found in one of the Bluetooth devices. // Now we can make a connection and start requesting jokes. try { String url; url = bluetooth_service_record.getConnectionURL( 0, false ) ; local_device_name = LocalDevice.getLocalDevice().getFriendlyName() ; connection_to_server = (L2CAPConnection) Connector.open( url ) ; while ( client_thread_should_be_run == true ) { if ( joke_requesting_enabled == true ) { send_to_server( local_device_name + " wants a joke." ) ; byte[] received_bytes = new byte[ 1000 ] ; // The L2CAPConnection method receive() will wait here // until the server sends the joke text. int number_of_received_bytes = connection_to_server.receive( received_bytes ) ; String received_data_as_string = new String( received_bytes, 0, number_of_received_bytes ) ; host_midlet.add_to_textbox( "\nFROM SERVER: " + received_data_as_string ) ; joke_requesting_enabled = false ; } else { // No joke requesting command has been given // Let's wait a little bit try { Thread.sleep( 500 ) ; } catch ( InterruptedException caught_interrupted_exception ) { } } // System.out.print( " C" ) ; } } catch ( IOException caught_ioexception ) { System.out.println( caught_ioexception ) ; } } host_midlet.add_to_textbox( "\nClient thread terminated." ) ; } public void stop_jokes_client() { client_thread_should_be_run = false ; } public void enable_joke_requesting() { joke_requesting_enabled = true ; } private void send_to_server( String string_to_send ) { byte[] bytes_to_send = string_to_send.getBytes() ; try { connection_to_server.send( bytes_to_send ) ; } catch( IOException caught_ioexception ) { System.out.println( caught_ioexception ) ; } } } public class BluetoothJokesMIDlet extends MIDlet implements CommandListener { private Display midlet_display = Display.getDisplay( this ) ; private String[] selectable_operation_modes = { "Jokes Server", "Jokes Client" } ; private List operation_modes_list = new List( "SELECT OPERATION MODE:", List.IMPLICIT, selectable_operation_modes, null ) ; TextBox textbox_for_messages = new TextBox( "", "", 2048, TextField.ANY ) ; Command exit_command = new Command( "Exit", Command.EXIT, 1 ) ; Command get_joke_command = new Command( "Get Joke", Command.SCREEN, 1 ) ; BluetoothJokesServer bluetooth_jokes_server ; BluetoothJokesClient bluetooth_jokes_client ; public BluetoothJokesMIDlet() { operation_modes_list.setCommandListener( this ) ; textbox_for_messages.addCommand( exit_command ) ; textbox_for_messages.setCommandListener( this ) ; } public void startApp() { midlet_display.setCurrent( operation_modes_list ) ; } public void pauseApp() { } public void destroyApp( boolean unconditional_destruction_required ) { } public void commandAction( Command given_command, Displayable display_content ) { if ( given_command == List.SELECT_COMMAND ) { if ( operation_modes_list.getSelectedIndex() == 0 ) { textbox_for_messages.setTitle( "JOKES SERVER" ) ; midlet_display.setCurrent( textbox_for_messages ) ; bluetooth_jokes_server = new BluetoothJokesServer( this ) ; } else if ( operation_modes_list.getSelectedIndex() == 1 ) { textbox_for_messages.setTitle( "JOKES CLIENT" ) ; textbox_for_messages.addCommand( get_joke_command ) ; midlet_display.setCurrent( textbox_for_messages ) ; bluetooth_jokes_client = new BluetoothJokesClient( this ) ; } } else if ( given_command == get_joke_command ) { if ( bluetooth_jokes_client != null ) { bluetooth_jokes_client.enable_joke_requesting() ; } } else if ( given_command == exit_command ) { if ( bluetooth_jokes_server != null ) { bluetooth_jokes_server.stop_jokes_server() ; } if ( bluetooth_jokes_client != null ) { bluetooth_jokes_client.stop_jokes_client() ; } destroyApp( true ) ; notifyDestroyed() ; } } protected void add_to_textbox( String string_to_textbox ) { String new_content_of_textbox = textbox_for_messages.getString() + string_to_textbox ; if ( new_content_of_textbox.length() > textbox_for_messages.getMaxSize() ) { new_content_of_textbox = new_content_of_textbox.substring( new_content_of_textbox.length() - textbox_for_messages.getMaxSize()); } textbox_for_messages.setString( new_content_of_textbox ) ; } } /******* // NOTE: in an early version of this program the client side was // constructed by using the following classes. I do not know what // is the benefit of using separate classes to implement the // DiscoveryListener interface. class InquiryListener implements DiscoveryListener { public Vector cached_devices = new Vector() ; // Called when a device is found during an inquiry. // An inquiry searches for devices that are discoverable. // The same device may be returned multiple times. public void deviceDiscovered( RemoteDevice bluetooth_device, DeviceClass class_of_device ) { int major = class_of_device.getMajorDeviceClass(); // ??????? if( ! cached_devices.contains( bluetooth_device ) ) { cached_devices.addElement( bluetooth_device ); } } public void inquiryCompleted( int discovery_type ) { synchronized( this ) { this.notify() ; } } public void servicesDiscovered( int transaction_ID_of_service_search, ServiceRecord[] list_of_services_found ) {} public void serviceSearchCompleted( int transaction_ID_of_service_search, int response_code ) {} } class ServiceListener implements DiscoveryListener { // The ServiceRecord interface describes characteristics // of a Bluetooth service. public ServiceRecord bluetooth_service_record ; public ServiceListener() { } // ???? // Called when service(s) are found during a service search public void servicesDiscovered( int transaction_ID_of_service_search, ServiceRecord[] list_of_services_found ) { bluetooth_service_record = list_of_services_found[ 0 ] ; System.out.println("foundService"); } public void serviceSearchCompleted( int transaction_ID_of_service_search, int response_code ) { synchronized( this ) { this.notify() ; } } public void deviceDiscovered( RemoteDevice bluetooth_device, DeviceClass class_of_device ) { } public void inquiryCompleted( int discovery_type ) { } } class BluetoothClient implements Runnable { private BluetoothJokesMIDlet host_midlet; private InquiryListener inquiry_listener; private ServiceListener service_listener; private boolean client_thread_should_be_run = true; private String local_device_name; private L2CAPConnection connection_to_server ; public BluetoothClient( BluetoothJokesMIDlet given_midlet ) { host_midlet = given_midlet; Thread thread_to_run_jokes_client = new Thread( this ) ; thread_to_run_jokes_client.start(); } public void stop_jokes_client() { client_thread_should_be_run = false ; } public void run() { host_midlet.add_to_textbox( "Starting client...\n" ) ; try { LocalDevice local_device = LocalDevice.getLocalDevice() ; DiscoveryAgent discovery_agent = local_device.getDiscoveryAgent() ; local_device.setDiscoverable( DiscoveryAgent.GIAC ) ; inquiry_listener = new InquiryListener() ; synchronized( inquiry_listener ) { discovery_agent.startInquiry( DiscoveryAgent.GIAC, inquiry_listener ) ; try { inquiry_listener.wait() ; } catch ( InterruptedException e ) { } } Enumeration bluetooth_devices = inquiry_listener.cached_devices.elements(); if ( bluetooth_devices.hasMoreElements() == false ) { host_midlet.add_to_textbox( "\nNo Bluetooth devices discovered." ) ; } UUID[] set_of_UUIDs_that_will_be_searched_for = { new UUID( "00000000000010008000006057028A06", false ) }; int set_of_attributes[] = { 0x0100 }; service_listener = new ServiceListener(); while( bluetooth_devices.hasMoreElements() ) { synchronized( service_listener ) { RemoteDevice bluetooth_device_on_list = (RemoteDevice) bluetooth_devices.nextElement() ; try { host_midlet.add_to_textbox( "\nFOUND: " + bluetooth_device_on_list.getFriendlyName( false ) ) ; } catch ( IOException caught_ioexception ) { host_midlet.add_to_textbox( "\n Found unidentified remote device ???!!!" ) ; } discovery_agent.searchServices( set_of_attributes, set_of_UUIDs_that_will_be_searched_for, bluetooth_device_on_list, //(RemoteDevice)bluetooth_devices.nextElement(), service_listener ) ; try { service_listener.wait() ; } catch (InterruptedException e) { } } } } catch ( BluetoothStateException e ) { System.out.println( e ) ; } if ( service_listener.bluetooth_service_record != null ) { try { String url; url = service_listener. bluetooth_service_record.getConnectionURL( 0, false ) ; local_device_name = LocalDevice.getLocalDevice().getFriendlyName() ; connection_to_server = (L2CAPConnection) Connector.open( url ) ; send_to_server( local_device_name + " wants a joke." ) ; byte[] received_bytes = new byte[ 1000 ] ; while ( client_thread_should_be_run ) { if ( connection_to_server.ready() ) { int number_of_received_bytes = connection_to_server.receive( received_bytes ) ; String received_data_as_string = new String( received_bytes, 0, number_of_received_bytes ) ; host_midlet.add_to_textbox( "\nFROM SERVER: " + received_data_as_string ) ; client_thread_should_be_run = false ; } } } catch ( IOException g ) { System.out.println( g ) ; } } host_midlet.add_to_textbox( "\nClient thread terminated." ) ; } private void send_to_server( String string_to_send ) { byte[] bytes_to_send = string_to_send.getBytes() ; try { connection_to_server.send( bytes_to_send ) ; } catch( IOException caught_ioexception ) { System.out.println( caught_ioexception ) ; } } } *****/