Введение
Не так давно вышедший Galaxy S3, помимо прочих нововведений в области аксессуаров, получил новый, ужасно скучный автомобильный док. В отличие от предыдущих аппаратов линейки (S, S2, Note), этот был сделан «универсальным», и потому не имел специального резистора, заставляющего телефон переключаться в автомобильный режим. А я, как человек, пользовавшийся в своё время полноценной док-станцией для первого SGS, уже привык, что при установке в док у меня включается режим громкой связи и появляется альбомная ориентация в TouchWiz. Но в новом доке такого достичь было нельзя…
Однако, решение было найдено, и потребовало лишь одной NFC-метки, рута, а также нескольких часов поисков и программирования.
Поиск решения
Первое, что я сделал, ещё когда у меня не было этого «универсального» дока — это разобрал док-станцию от первого SGS. Результат был достаточно близок к желаемому, хотя выглядел довольно забавно:
На картинке видно, что в области уведомлений красуется «автомобильная» иконка — так вот, она не имеет никакого отношения к Driving Mode (который S-Voice), это именно уведомление от самого Android.
Однако, и сам SGS, и его док пришлось отдать, а потом ко мне приехал новый док, и вопрос переключения режима встал уже в полный рост. Очевидное решение заключалось в использовании NFC-метки для включения автомобильного режима, но как этого добиться? В Google Play есть приложения, которые якобы включают Car Mode, но по факту они используют стандартный метод UiModeManager, который всего лишь запускает Car Home, а столь желаемые мной громкая связь и альбомная ориентация не включаются.
В документации по Android есть стандартный метод определения того, находится ли устройство в док-станции:
registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT)).getIntExtra(Intent.EXTRA_DOCK_STATE, -1);
а также упомянут метод, который это состояние устанавливает:
sendStickyBroadcast((new Intent(Intent.ACTION_DOCK_EVENT)).putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_CAR));
однако, в реальной программе он работать не будет, т.к. конкретно это событие требует специальных прав (ведь оно, по сути, генерируется USB-контроллером). Но даже имея root, как это можно сделать?
Есть простое и достаточно известное решение:
su -c /system/bin/am broadcast -a android.intent.action.DOCK_EVENT --ei android.intent.extra.DOCK_STATE 2
которое, увы, неполно. Да, этот вызов успешно включит полноценный автомобильный режим, однако из-за того, что это обычный broadcast, а не «sticky» (утилита am не умеет этого), вышеупомянутый метод определения состояния док-станции работать не будет, поэтому придётся либо заводить в приложении две кнопки («включить» и «выключить»), либо найти способ послать полноценное системное сообщение.
Немного покопавшись в исходниках Android, можно найти класс DockObserver, который как раз занимается отправкой этого сообщения. Изучив его, легко видеть, что на самом деле он делает чуть-чуть больше, чем просто оповещает о смене режима (например, воспроизводит звук вставки в док), а также то, что делает это он при поступлении особого события (UEvent). То есть, если научиться такое событие создавать, то можно сымитировать не только следствие, но и причину!
Ну и, делается это довольно простым кодом с использованием NDK:
char event[] = "ACTION=changeDEVPATH=/devices/virtual/switch/dockSUBSYSTEM=switchSWITCH_NAME=stateSWITCH_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);
}
Осталось только упаковать эту утилиту в приложение, читающее текущее состояние описанным выше стандартным способом и переключающее его консольным вызовом из-под рута.
Заключение
Итак, само приложение доступно в Google Play.
При запуске оно переключает телефон в автомобильный режим, при повторном запуске — выходит из него.
Если повесить его запуск на NFC-метку, можно получить тот самый полноценный автомобильный док.
Ну и, все исходники можно найти на GitHub.
Автор: ginkage