Lecture 18 Bluetooth

In this chapter you will learn about some of the pieces for creating a connection between two co-located devices using Bluetooth. This will let you gain some familiarity with the Bluetooth API, as well as further practice working with Intents.

The code for this tutorial can be found at https://github.com/info448/lab-bluetooth.

This tutorial involves filling in the remaining pieces from a Sample Project provided by Google. Google includes lots of samples demonstrating how to use particular pieces of functionality; reading and adapting the provided projects is a great way to learn new skills. There are a lot of comments, though that sometimes makes it hard to follow all the pieces. Read carefully!

The emulator doesn’t support Bluetooth, so you will need to run this project on a physical device!

Your task is to fill in the missing pieces of code, following the instructions below. I’ve marked each location with a TODO comment, which should show up in blue in Android Studio.

  1. Start by reading through The Basics to get a sense for what classes will be used and what their roles are. You only need to focus on the first 4: BluetoothAdapter, BluetoothDevice, BluetoothSocket, and BluetoothServerSocket (the rest are for other kinds of Bluetooth connections, like audio transfer and stuff). You don’t need to know all the methods or details of these classes, but should be familiar with their general, one-sentence purposes!

  2. You’ll need to request permission to use Bluetooth. Add the appropriate <uses-permission> attributes: one for BLUETOOTH (for communication; included) and one for BLUETOOTH_ADMIN (to “discover” devices and make connections).

  3. The main UI is defined in the BluetoothChatFragment class, which is a Fragment that holds the chat system. Start by filling in the onCreate() callback by fetching the default Bluetooth adapter and saving it to an instance variable (mBluetoothAdapter). If the adapter doesn’t exist (is null), you should Toast a message that Bluetooth isn’t available (using the Activity's Application Context so that the Toast lasts), and then call finish() on the Fragment’s Activity (to close the application).

  4. You’ll want your app to make sure that the user has Bluetooth turned on. In the Fragment’s onStart(), check whether the the BluetoothAdapter is enabled. If not, you’ll want to prompt the user to enable it, such as by launching the “Settings” app. Create an Implicit Intent for the action BluetoothAdapter.ACTION_REQUEST_ENABLE, and send this Intent for a result (with the result code of REQUEST_ENABLE_BT). Look in the Fragment’s onActivityResult() method to see what happens when we get a response back!

    • The BluetoothChatService (stored in the instance variable mChatService) is an object representing a “background service”—think an AsyncTask but with a much longer lifespan. This particular service handles sending bytes of data back and forth over Bluetooth. We’ll talk about Services more later in the course.
  5. In order for a device to connect to yours over Bluetooth, your device will need to be discoverable: effectively, it has to respond to public queries about its existence (sort of like having your instant messaging status as “Online/Available”). In the Fragment’s ensureDiscoverable() helper method, check if the device is currently discoverable by calling getScanMode() on the BluetoothAdapter; it should return a value of BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE.

    • If this IS NOT the case, then you should send another Implicit Intent (start an Activity, though not for a result) to handle the BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE action.

    This intent should include (put) an extra that has the key BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION and a value of 300, so that we are in “discoverable” mode for 300 seconds.

  6. The discovery of devices is controlled by the DeviceListActivity Activity. This is a separate Activity that will actually appear as a popup dialog (though it doesn’t use DialogFragment; it just “themes” the Activity as a dialog in the Manifest). The Activity’s onCreate() does a lot of UI work (including setting up an Adapter!), but it also needs to set up a BroadcastReceiver to listen for events like when devices are found. (This is the equivalent of declaring a <receiver> and <intent-filter> in the Manifest, but we need to do it in Java since the Receiver isn’t a separate class and since we want to do it dynamically).

    • First instantiate a new IntentFilter object (giving it the BluetoothDevice.ACTION_FOUND action).

    • Then use the registerReceiver(receiver, intentFilter) method, passing it the already-existing receiver (mReceiver) and the IntentFilter you just created!

    • Then repeat the above two steps, but this time for the Bluetooth.ACTION_DISCOVERY_FINISHED action. This will register an additional IntentFilter on the same receiver.

  7. We can actually begin searching for devices by filling in the Activity’s doDiscovery() helper method (which is called when the Scan button is pressed).

    • Add a check to see if the BluetoothAdapter currently isDiscovering(). If so, then you should tell the adapter to cancelDiscovery().

    • Whether or not the check was true (so even if we canceled the discovery), tell the adapter to startDiscovery() to begin searching for devices!

  8. Once the user has selected a device to connect to, we handle that connection back in the BluetoothChatFragment. Fill in that class’s connectDevice() helper method to connect to the device!

    • First you’ll want to get the device’s “address” (a MAC address that acts as a unique identifier) from the Intent’s extras: get the Bundle of extras from the Intent, then get the String with the key DeviceListActivity.EXTRA_DEVICE_ADDRESS.

    • You can then find the device (a BluetoothDevice object) by calling the .getRemoteDevice() method on the BluetoothAdapter and passing this address.

    • Finally, you can use the mChatService’s .connect() method to connect to this device (passing down the secure option as a second parameter). The BluetoothChatService#connect() method creates a new Thread to do the communication work, and opens up network sockets so that messages can be passed between the devices. (This is actually part of the hard part or working with Bluetooth; luckily we have a class to abstract that for us!)

  9. The last part is to actually send a message! In the sendMessage() helper in BluetoothChatFragment, fill in the details so that the String can be sent to the socket in the chat service.

    • First you need to convert the message String into a byte[] (for communication over the socket). Use the String’s getBytes() method to convert.

    • Then you can tell mChatService to .write() those bytes!

    • We then need to reset the mOutStringBuffer instance variable (which keeps track of the message that has been typed so far). Use .setLength() to give it a length of 0, which will effectively make it empty.

    • And finally, because we’ve changed the outgoing message, set the text of the mOutEditText TextView to be the (now empty) mOutStringBuffer.

And that’s it! You should now have a working chat system! Search for and connect to someone else’s device and try saying “hello”!

When you finish the lab, you should probably turn off the Bluetooth radio on your device. Android’s Bluetooth implementation has critical security flaws that can be hacked to execute malicious code. Unless you’ve patched with the latest security update (meaning you have the latest device), it is recommended that you avoid using Bluetooth.