In 2016, a BMW joined my household, and I had my first taste of IDrive and BMW Connected Apps. Through some magic Bluetooth protocol, Spotify on my phone would be added to the list of music sources in the dashboard. Clicking into this entry showed a rich user interface, offering browse options to select playlists and to start radio stations based on the current song, far more interactive than the basic Bluetooth music controls.
This immediately ignited excitement for the possibilities! Instead of being stuck with what apps are loaded on the car at time of manufacturer, the car can automatically be upgraded as support is added to the respective phone apps. Instead of needing to take my phone out of my pocket and stare down at the phone screen to switch music, I can use the tactile controller knob with the infotainment screen to safely control any app!
However, as is normal with Bluetooth, the experience was not very smooth. Sometimes the additional Bluetooth Apps protocol wouldn't connect, and sometimes the individual apps themselves wouldn't be responsive. Cries for help on the Spotify forums were ignored, and the BMW Connected app has terrible reviews with no signs of fixing anything.
Additionally, BMW Connected for Android only shared a very limited selection of apps, compared to the available apps on iPhone: only Spotify and iHeartRadio, along with a basic Calendar app. Since I enjoy other music apps, I called up BMW Support and asked if I could get access to the BMW Ready SDK so I could build my own apps. They declined.
So, I decided to figure out this BMW Apps protocol and add my own music apps to the system, without their help.
How hard could it be? Bluetooth is a standard protocol, I just have to learn what to say to the car and build up from there!
Oh hey look, Android has built-in Bluetooth Capture logging! I'll just record what the phone app is saying to the car and see what I find:
This SPP Protocol seemed to have lots of juicy information: I saw some X509 certificates, some XML data, some strings that look like song metadata, a ton of stuff!
I started noticing a pattern in the first bytes of most packets: The 0th byte was 0, the 1st byte was 1 or 6, the 2nd byte was 0, the 3rd byte was usually very low, and the next bytes were almost always 0x0FA4. I figured out that the next 2 bytes were the length of the remaining data, and the next 4 bytes of data were almost always 0xDEADBEEF.
I began writing a Wireshark Lua plugin to help me understand the data, parsing the first bytes as 4 16-bit values named Val1, Val2, Val3, and Length, and then outputting the remaining bytes of data.
It seems that this protocol is used to multiplex connections into a single Bluetooth serial socket, with Val2 being different connection IDs. With this field being parsed out in Wireshark, I could use display filters to follow an individual communication flow.
After a little bit of research, I discovered an article explaining that BMW uses Apache Etch as "the fundamental communication protocol used for BMW Apps". A quick trip to the Apache Etch docs confirms that 0xDEADBEEF is the magic identifier at the start of every Etch RPC call. This means, then, that I just have to decode each BCL stream to Apache Etch packets, and Wireshark's built-in Etch parsing will take over!
Except, of course, that Apache Etch compiles each function name, and any other symbol names, into a 32-bit hash value. Wireshark can use an Etch debug artifact to replace the hash value with pretty names, but I first needed to figure out the names and hash them myself. The Etch hash algorithm is public, so I wrote a Rust implementation to help generate this debug name artifact manually, since I don't have the original Etch IDL.
But, where to get the names?
Turns out JVM bytecode (which Android apps are equivalently written in) is very easy to decompile. Variable names are obfuscated a bit, but the Etch generated classes contain an exact list of all the available Etch symbols: