Since my last post, I have determined that my goal is, practically, not achievable. To reach this conclusion required hours of Android tutorials, as I had never written an Android application before, extensive reading of documentation and forums, and in the end I went through two redesigns of the architecture of my plugin.
The first redesign came very early on, when I realized Android’s framework was more complex than I had expected. I then followed numerous tutorials found here. Once I had gained a much better understanding of the framework I was working in, I performed my first redesign, and attempted to utilize a ServiceIntent to perform the work done by the plugin in a background thread.
This presented two challenges. First, the ServiceIntent required use of the support-v4 library. This took some time to track down and package with my plugin, as Unity3D did not package it automatically. Second, I discovered that because of scoping and packaging, I was unable to get Unity’s Action to communicate with my ServiceIntent Action. I tried to get this to work by modifying Unity’s AndroidManifest.xml file, but my approach did not work. There may be another way to do this that someone more experienced with Android would be aware of, but in the end I decided to simplify my plugin by running everything on the same thread as Unity, and redesigned my plugin a second time.
My second redesign was much more successful as far as my project goals were concerned. By running all operations in the Context of Unity’s Action, I was able to make significant headway toward communicating with a wiimote. From Unity’s perspective, I exposed methods to get errors and status updates from the plugin, and a method to initiate connection with a device. Everything else was done on the plugin side.
By pressing the connect button in the Unity interface, the plugin checks for Bluetooth being enabled and requests enabling if it is not. It then creates a BroadcastListener and IntentFilter to register Intents from the system related to Bluetooth Discovery operations, then initiates discovery. The handler for discovery related intents watches for discovered devices with the “Nintendo” name, and if it finds one, it stops the discovery process and attempts to pair the devices.
Pairing involves another BroadcastListener and IntentFilter pair. One of the pairing operations that is received is a request for a PIN from the wiimote, which is calculated by reversing the MAC address of the wiimote and converting the bytes to ASCII. I was able to override Android’s UI and automatically force the setting of the calculated PIN. Once authenticated, the devices can skip the pairing after the discovery process in the future as both devices remember each other.
This is where I ran into a brick wall, however. Attempting to actually open a socket connection with the device returns a “Protocol not supported” error. Simply put, Android only has support for the GATT and RFCOMM protocols, and the wiimote only uses the L2CAP protocol. Support for the L2CAP protocol was removed when Android changed the Bluetooth software stack in Android 4.2. It appears that the only way to fix this issue would be to contribute to the Android Open Source Project and implement the L2CAP protocol in the Bluetooth stack. Since my goal was to create an easy way for a wide audience to add a motion controller to their Android phone for use with Google Cardboard, requiring users to replace their OS with a custom one is unfeasible. In addition, adding a new protocol to an open source project I’m not familiar with is likely outside the scope of what can be accomplished in the remainder of the semester.
A brief search has not turned up any motion controllers that utilize RFCOMM or GATT. Using a second Android phone as a controller is a possibility, but comes back to the issue of accessibility, as most people (including myself) do not own a second Android phone.