[Galaxy S III Themes and Apps] ginkage:
Preface

Like some other devs around here, I was wondering if there's a good way of forcing my phone into the Car Dock mode, which not only auto-launches Car Home application, but also turns on loudspeaker, and also allows TouchWiz to operate in landscape mode (which is kinda useful in dock).

Research (skip this section if you are not interested):

The right way to do this is via a hardware JIG (like here). I had a thing like this once, it came from a dissected Galaxy S dock, and worked fine (or at least funny).

Later on, I had to give the dock away, and got myself a Universal Samsung dock (like this one). It's fine to hold my phone and charge it, but I have already tasted the complete dock experience, so that's what I wanted to achieve...

First, I've installed an NFC tag on this dock.

Second, I used some Car Mode app from Google Play, which turned out useless: it used a UiModeManager API, which only launches Car Home, but doesn't do the auto-loudspeaker and other things.

There is a known, proper solution of achieving that functionality, which is running this command:

su -c /system/bin/am broadcast -a android.intent.action.DOCK_EVENT --ei android.intent.extra.DOCK_STATE 2

(under root, of course). The problem was, I wanted my app to send a "sticky" broadcast message (like a real dock does), so that a

registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT)).getIntExtra(Intent.EXTRA_DOCK_STATE, -1);

call would return me a correct dock state (as written in Android SDK docs). My search on how to do this got no result though.

An API call for that would be something like

sendStickyBroadcast((new Intent(Intent.ACTION_DOCK_EVENT)).putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_CAR));

but it is only possible to send this message from within a firmware... Bummer.

But hey, we can go deeper, and look at the firmware itself!

So, the Android source code shows us a file named DockObserver.java, which contains the code that sends the message I need (right now it uses sendStickyBroadcastAsUser from API Level 17, which is still "not available to applications that are not pre-installed on the system image"). Looking closer, there's an UEvent handler that actually sends it. So, basically, if we send that UEvent, it will not only imitate an effect, it will imitate the cause, and everything will work even better than I wanted it to (e.g. it can play a docking sound)! That's what my first NDK utility did:

char event[] = "ACTION=change\0DEVPATH=/devices/virtual/switch/dock\0SUBSYSTEM=switch\0SWITCH_NAME=state\0SWITCH_STATE=2";
int sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);

if (sock != -1) {
struct sockaddr_nl snl;
struct iovec iov = { event, sizeof(event) };
struct msghdr msg = { &snl, sizeof(snl), &iov, 1, NULL, 0, 0 };

memset(&snl, 0, sizeof(struct sockaddr_nl));
snl.nl_family = AF_NETLINK;
snl.nl_pid = getpid();
snl.nl_groups = -1;

sendmsg(sock, &msg, 0);
close(sock);
}

The rest was simple: bundle this utility with a simple java application, which reads current dock state (as described above) and switches it via "su -c <utility> <mode>" call.

Conclusion:

So, the app itself is available in Google Play (or you can download the attached file).

Run it once, you're in a Car Dock mode. Run again, you're back to normal.

Assign its launch to an NFC sticker, and you'll get a fully-featured virtual car dock. :)

As a bonus, you may find the source code on GitHub.