Bluetooth Blues

I spent the better part of two days working on Bluetooth connectivity for an Android app I’m developing. Going into it, I had virtually no experience working with Bluetooth, especially on Android. I quickly discovered some of the peculiarities of the platform’s Bluetooth API.

In addition to connecting to Bluetooth devices, the client wanted to pair and unpair from the app. The easy way out, and probably The Android Way™, would be to pass that responsibility off to the OS, à la an Intent:

This will bring up the Bluetooth settings menu, from which you can pair/unpair devices, but the problem is that it’s a complete context switch for the user—they are no longer in your application. I was looking to provide a more seamless experience so that the user didn’t have to leave the app at all to pair a device.

Device Discovery

The entry point for Bluetooth interaction in Android is through the BluetoothAdapter, which is used to orchestrate the device discovery process and fetch paired devices. Calling startDiscovery() will tell the adapter to start scanning for devices, and when one is found, an Intent will be fired off which can then be intercepted by a BroadcastReceiver.

The above code shows how the device discovery process is kicked off and how a BroadcastReceiver is registered to listen for discovery Intents. Note that the BroadcastReceiver is unregistered and discovery is canceled in onDestroy.

In order to react to discovery events, we must implement a BroadcastReceiver.

Device Pairing

Once you have a handle on the BluetoothDevice received in the BroadcastHandler, how do you actually pair with it? Looking at the documentation for the class, you’ll see that there are no methods for doing this. This is where things start to get a little strange.

Diving into the source code for BluetoothDevice, you’ll actually find that there is functionality for doing pairing and unpairing, but the methods are hidden from the API using the @hide annotation. What’s more interesting is that the methods are, in fact, public.

Evidently, device pairing is intended to be performed only by platform applications, which is a little curious considering the permission needed to perform pairing, android.permission.BLUETOOTH_ADMIN, is accessible by third-party applications. Nonetheless, this means we actually can pair a BluetoothDevice, just not in the way the Android engineers intended.

To access the BluetoothDevice methods needed, createBond and removeBond, we can use reflection.

The pairDevice method will prompt the user to enter a PIN for the discovered device, circumventing the need to open the Bluetooth settings. As such, the pairing does not actually complete until the correct PIN is entered. The boolean value returned from the method indicates whether the pairing process was successfully kicked off or not.

It goes without saying that this code, while functional, is volatile because these methods are technically not part of the public API, so they could change or disappear in future platform releases.

We can add an Intent filter to our BroadcastReceiver to listen for pairing events using the action BluetoothDevice.ACTION_BOND_STATE_CHANGED.

There are a few other hidden methods in BluetoothDevice, like cancelPairingUserInput, setPairingConfirmation, convertPinToBytes, and setPin, that you could potentially use to customize the pairing process or perform it programmatically, but use them at your own risk.

Once the devices are paired, they can be connected using one of BluetoothDevice’s createRfcommSocketToServiceRecord or createInsecureRfcommSocketToServiceRecord methods after determining the UUID to use, either with getUuids or fetchUuidsWithSdp (or, in most cases, using the well-known UUID 00001101-0000-1000-8000-00805F9B34FB).

It’s very likely that Android’s Bluetooth API is subject to change soon. It already has changed in some of the more recent releases, although I’m not entirely sure why Google isn’t providing a stable API for pairing. Jelly Bean 4.2 introduces a new Bluetooth stack, moving from BlueZ to a Broadcom solution, so my guess is that it’s related to this.

Implementing Spring-like Classpath Scanning in Android

One of the things that Spring 2.5 introduced back in 2007 was component scanning, a feature which removed the need for XML bean configuration and instead allowed developers to declare their beans using Java annotations. Rather than this:

We can do this:

It’s a pretty simple idea since Java makes it very easy to introspectively check a class’s annotations at runtime through its reflection API. Spring’s component scan feature also allows you to specify the base package(s) to scan for beans.

The big question is how do we get access to the classes in the classpath, specifically, those in the desired package? Java SE doesn’t provide an API for doing it, but there are ways to accomplish this. The most common (if not the only) approach is to load classes by relying on the file system. We know that we can use the ClassLoader to load a class by its package-qualified name, so it becomes a matter of retrieving the file names.

Getting the classpath itself in Java SE is easy:

This will yield something that looks like “/Users/Tyler/Workspace/Test/bin:/Users/Tyler/Workspace/Test/lib/gson-2.1.jar”. Loading the files from here is pretty straightforward, as is filtering on the package name since it maps to a directory one-to-one.

Another similar approach is to use the ClassLoader to load the resources directly:

Transition to Android

Unfortunately, these solutions don’t lend themselves to Android, which made implementing classpath scanning a little more difficult for Infinitum. The reason for this is, more or less, because of the way Android’s Dalvik VM is designed. When an Android application is compiled, the Dalvik bytecode is packaged into a file called “classes.dex” inside the APK. The good news is that the Android SDK provides an API for interacting with DEX files through the DexFile class.

In order to access classes.dex, we need a handle on the APK itself, which is actually quite easy to do:

The above code opens a DexFile for the running APK. Of course, this can have some performance implications. Opening the DexFile will potentially cause the VM to pass classes.dex through a process known as “dexopt”, which is a program that performs bytecode verification and optimization. This is an expensive process, but since we’re opening a DexFile for the APK itself, classes.dex should have already undergone this process, meaning dexopt won’t be run again.

The DexFile gives us access to the classes contained in classes.dex as an enumeration of strings representing the package-qualified class names. With this, we can iterate over the class names and load any which match the desired package.

This gets the job done, and it’s essentially how Infinitum accomplishes component scanning. However, it’s a very expensive operation. DexFile.entries() yields every class in the classpath — that is, every class in classes.dex — which includes not just application binaries, but also those of any libraries included.

It’s great that we can introspect every class in the classpath, but if we’re only interested in classes of a particular package, we’re out of luck. Every class is compiled into classes.dex and, short of decompiling it ((Tools for decompiling DEX files exist, such as Baksmali, but doing such a thing at runtime — if it’s even possible — would arguably not gain you any performance benefits. Still, this is something worth exploring.)),  there’s no way to pull out the classes we want without iterating over the entire classpath.

So, for now we settle with this somewhat inefficient solution. Nonetheless, it accomplishes what it needs to at the cost of maybe a few hundred milliseconds ((On the emulator running on my MacBook Pro, the classpath scanning takes about 600 milliseconds, while on my Galaxy Nexus, it takes about 200 milliseconds.)), so maybe it’s not such a bad approach in the grand scheme of things.

Dalvik Bytecode Generation

Earlier, I discussed the use of dynamic proxies and how they can be implemented in Java. As we saw, a necessary part of proxying classes is bytecode generation. From its onset, something I wanted to include in Infinitum was lazy loading. I also wanted to provide support for AOP down the road. Consequently, it was essential to include some way to generate bytecode at runtime.

The obvious choice would be to use a library like Cglib or Javassist, but sadly neither of those would work. That’s because Android does not use a Java VM, it uses its own virtual machine called Dalvik. As a result, Java source code isn’t compiled into Java bytecode (.class files), but rather Dalvik bytecode (.dex files). Since Cglib and Javassist are designed for Java bytecode manipulation, they do not work on the Android platform. ((ASMDEX, a Dalvik-compatible bytecode-manipulation library was released in March 2012, meaning Cglib could, in theory, be ported to Android since it relies on ASM.))

What’s a programmer to do? Fortunately, some Googlers developed a new library for runtime code generation targeting the Dalvik VM called Dexmaker.

It has a small, close-to-the-metal API. This API mirrors the Dalvik bytecode specification giving you tight control over the bytecode emitted. Code is generated instruction-by-instruction; you bring your own abstract syntax tree if you need one. And since it uses Dalvik’s dx tool as a backend, you get efficient register allocation and regular/wide instruction selection for free.

Even better, Dexmaker provides an API for directly creating proxies called ProxyBuilder. If you followed my previous post on generating proxies, then using ProxyBuilder is a piece of cake. Similar to Java’s Proxy class, ProxyBuilder relies on an InvocationHandler to specify a proxy’s behavior.

Dexmaker enabled me to implement lazy loading and AOP within the Infinitum framework. It also opens up the possibility of using Mockito for unit testing in an Android environment because Mockito relies on proxies for generating mocks. ((Infinitum is actually unit tested using Robolectric, which allows for testing Android code in a standard JVM.))