Filed under General
Today, we just got a swath of Nexus S phones to support our research class’s exploration of uses for NFC technology. Unfortunately for me, I recently bought a Motorola Atrix which doesn’t have any tag reading or writing capability. It’s only sort of unfortunate though, because the device is amazing. It has a great screen and is wicked fast. But, I digress, the important thing is that I’m able to use that brand new device for social interactions triggered by a physical context. Luckily for us, Ben showed us how to do this if at least one of the two parties has active NFC technology. Basically, you put a smart label on the “dumb” phone and then run a small piece of software on it that lets the “smart” phone contact it over bluetooth (or the internet) to complete the interaction.
Google added support for “insecure” RFCOMM sockets in the 2.3 version of Android, however, my phone is currently stuck at 2.2 of the OS thanks to a locked boot loader. This leads to an obnoxious UI foible when performing the first bump operation between phones. Because the Froyo software doesn’t include a public interface for Bluetooth without PIN-based pairing, the initial NFC tag triggered chat between the devices causes notifications to popup that ask for validation of the Bluetooth pairing key. I set out to solve this problem in a way that will work for devices regardless of OS version and not require root or custom OS images.
The core parts of the Bluetooth connection initiation APIs are contained in the classes BluetoothAdapter and BluetoothDevice buried inside the core Android frameworks. A quick inspection of the source for those classes in Gingerbread shows that the insecure RFCOMM creation API calls are mostly thin wrappers around constructors for the BluetoothServerSocket and BluetoothSocket classes. These constructors have package visibility, so you can’t call them from outside java code directly. Looking at the 2.2 sources reveals that the signatures of the constructors match with the Gingerbread, so if I can just call those constructors with modified parameters then I can have “insecure” Bluetooth on my Atrix phone.
Java has some great reflection APIs, so it is possible to get handles to all of the internal methods and fields required to re-implement the pairing-free RFCOMM interface. There are two tricks to doing this. First, you have to call the Declared variants of functions that fetch handles to class members. These variants will allow you to get access to private and package visible members. Second, you have to call setAccessible(true) on the method and field handles that you retrieve so that you are allowed to violate the protections that the visibility modifiers imply. If you do those two things, the only remaining challenge is wrapping everything with the correct exception handling. Invocation of reflected methods introduces the possibility for a wide range of exceptions and it wraps exceptions triggered from within the invoked calls inside TargetInvocationException. Solving these issues just mean a bit of code bloat.
With those bits and pieces in mind, I produced a utility class that allows for non-Gingerbread devices to use the pairing-free Bluetooth (InsecureBluetooth.java). To test it out, I modified the BluetoothChat sample in the Android v8 SDK to use the class (BluetoothChatService.java … the only file I changed). I was able to establish a session between the Atrix and the Nexus S without incurring the wrath of the pairing user interface. Soon this will be integrated into our easy NFC packages available on github.
When you set up an insecure connection, it actually does create a pairing between the two devices. The assumption is that you are checking the identity of the other device out-of-band. For two devices connecting over NFC, you physically validated they wanted to connect. This workaround lets you make software which lets you achieve the Gingerbread level of UI smoothness for pairing if your higher level system handles identity checks on its own. For many of the projects we are working on, the higher level identity check is there, so its safe to use this to establish pairings. If for some reason the higher level check fails, the right thing to do is to un-pair the devices.