Введение
Сегодня я расскажу про то, как устроено взаимодействие с модемом в ОС Android. В данной статье описывается структура компонентов операционной системы Android, ответственных за сетевое взаимодействие по протоколам пакетной передачи данных – GPRS, EDGE, 3G и т.д.
Эта статья содержит большое количество теории, практика же будет во второй статье.
Описание слоя радиоинтерфейса
Рассмотрим, так называемый, слой радиоинтерфейса, от английского – Radio Interface Layer. В ОС Android представляет он представляет собой абстрактный слой между сервисом телефонии (android.telephony) и модемом.
Рисунок 1. Слой радиоинтерфейса.
На рисунке 1 красным цветом выделен слой радиоинтерфейса, который включает в себя:
- java слой радионитерфейса;
- RIL демон;
- планировщик событий;
- RIL производителя.
Компоненты слоя радиоинтерфейса выполняют следующие задачи:
Java слой радионитерфейса (RIL Java) отправляет запросы RIL демону через локальный Linux сокет /dev/socket/rild. Исходные коды RIL Java располагаются в файле /telephony/java/com/android/internal/telephony/gsm/RIL.java.
RIL демон загружает и инициализирует RIL производителя и планировщик событий. Исходные коды RIL демона располагаются в каталоге /hardware/ril/rild/.
Планировщик событий обрабатывает запрашиваемые и незапрашиваемые команды. Является посредником между RIL Java и RIL производителя. Исходные коды планировщика событий располагаются в каталоге /hardware/ril/libril/.
RIL производителя инициализирует модем, производит взаимодействие непосредственнос с модемом. RIL производителя представляет из себя динамическую библиотеку, разрабатываемую производителем модема. Шаблон для разработки располагается в каталоге /hardware/ril/reference-ril/. Cамыми популярными производителями модемов для мобильных устройств на сегодняшний день являются компании Intel и Qualcomm, а исходные коды библиотек не доступны.
Внутренняя структура RIL демона
На рисунке 2 изображена внутренняя структура RIL демона. Задача RIL демона – инициализировать слой радиоинтерфейса при запуске ОС. Изучив небольшой объём кода RIL демона, можно увидеть, что он осуществляет свою работу по следующему алгоритму:
1. Считывает из настроек системы путь к RIL производителя (rild.libpath) и свойства системы, которые используеются RIL производителем.
2. Загружает RIL производителя и запускает цикл событий в планировщике событий.
3. Вызывает функцию RIL_Init для инициализации и получения ссылок на функции RIL производителя.
4. Вызывает функцию RIL_register, для регистрации планировщиком событий ссылок на функции RIL производителя.
Рисунок 2. Внутренняя структура RIL демона.
Существует два вида взаимодействия между компонентами слоя радиоинтерфейса: запрашиваемые (solicited) и незапрашиваемые (unsolicited) команды. Запрашиваемые команды инициируются сервисом телефонии, например исходящий звонок, установление интернет соединения. Незапрашиваемые команды инициируются модемом, например входящее SMS сообщение, входящий звонок.
Связь между сервисом телефонии и планировщиком событий
Взаимодействие между RIL Java и планировщиком событий происходит через локальный Linux сокет /dev/socket/rild. Каждый запрос к уровню библиотек записывается в экземпляр класса com.android.internal.telephony.RILRequest, для сохранения информации о запросе, до тех пор, пока не будет получен ответ от нижележащих слоев радиоинтерфейса.
Параметры запроса записываются в структуру parcel, а сам запрос отправляется в класс com.android.internal.telephony.RIL.RILSender. Формат запроса указан на рисунке ниже:
Класс com.android.internal.telephony.RIL.RILReceiver прослушивает локальный Linux сокет, ожидая ответов от планировщика событий. RILReceiver получает два вида ответов: запрашиваемые и незапрашиваемые. Их формат представлен на рисунках ниже.
Рисунок 4. Формат запрашиваемого ответа.
Рисунок 5. Формат незапрашиваемого ответа.
Реализация RIL производителя
Для реализации RIL производителя компания-производитель модема разрабатывает динамическую библиотеку, которая реализует множество функций, необходимых операционной системе Android для обработки запросов, адресованных модему. Её название определяется следующим соглашением: libril-<название компании>-<версия RIL>.so
В заголовочном файле /include/telephony/ril.h содержится:
- описание множества структур, например: RIL_RadioState, RIL_Data_Call_Response;
- прототипы функций, например: RIL_Init, RIL_onRequestComplete;
- числовые определения для запрашиваемых и незапрашиваемых команд, например: #define RIL_REQUEST_SETUP_DATA_CALL 27.
Слой радиоинтерфейса является аппаратно независимым и RIL производителя может использовать любой протокол для взаимодействия с модемом. Как правило, используется множество стандартизованных Hayes AT-команд, тем не менее, некоторые производители модемов дополняют стандартный набор AT-команд своими собственными расширениями.
Для управления запрашиваемыми командами производитель модема должен реализовать набор функций, прототипы которых описаны ниже. Типы запросов запрашиваемых команд определены в заголовочном файле ril.h с префиксом RIL_REQUEST_.
-
void (*RIL_RequestFunc)(int request,void *data, size_t datalen,RIL_Token t)
это функция обработки запрашиваемых команд. Именно она обрабатывает запрашиваемые команды, которые определены в заголовочном файле ril.h и начинаются с префикса RIL_REQUEST_.
-
RIL_RadioState (*RIL_RadioStateRequest)()
эта функция возвращает текущее состояние модема.
-
int (*RIL_Supports)(int requestCode)
функция возвращает 1, в случае если данный код запроса поддерживается, и 0 в противном случае.
-
void (*RIL_Cancel)(RIL_Token t)
эта функция используется для индикации того, что незавершенный запрос будет отменён. Вызывается в отдельном потоке, отличном от того, в котором вызвана функция RIL_RequestFunc.
-
const char * (*RIL_GetVersion) (void)
возвращает версию RIL производителя.
RIL производителя использует следующие обратные вызовы для связи с RIL демоном:
-
void RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen)
сообщает о том, что запрашиваемая команда была обработана.
-
void RIL_requestTimedCallback (RIL_TimedCallback callback, void *param, const struct timeval *relativeTime)
функция отвечающая за промежуток времени, спусти который необходимо произвести обратный вызов.
Функция обратного вызова, которая используется производителем RIL для запуска незапрашиваемых команд должна иметь следующий вид:
void RIL_onUnsolicitedResponse(int unsolResponse, const void *data, size_t datalen);
Запрашиваемые команды
Существует 61 запрашиваемая команда. Их можно сгруппировать следующим образом:
- работа с SIM картой, вводом/выводом и уникальным идентификатором устройства (11);
- статус и обработка звонка (16);
- сетевой статус (4);
- сетевые настройки (12);
- SMS сообщения(3);
- соединение по протоколам пакетной передачи данных(4);
- питание и перезапуск (2);
- дополнительные сервисы (5);
- определения и поддержка производителя (4).
Рисунок 6 иллюстрирует последовательность вызовов на различных уровнях слоя радиоинтерфейса при поступлении запрашиваемой команды «установление интернет соединения»:
1. RIL Java вызывает RILSender.handleMessage() и отправляет планировщику событий через локальный Linux сокет /dev/socket/rild запрос с сгруппированными аргументами.
2. Планировщик событий последовательно вызывает listenCallback(), processCommandCallback(), proccessCommandBuffer(), производит предварительную обработку данных и вызывает dispatch(Request code)
3. В RIL производителя вызывается функция OnRequest(), в которой по коду запроса (SETUP_DATA_CALL) определяется какое действие должно осуществляться в дальнейшем. RIL производителя отправляет соответсвующую запросу последовательность AT-команд модему и принимает ответы на них. Заканчивает обработку кода запроса вызовом функции OnRequestComplete(). Если необходимо, отправляет ответ планировщику событий.
4. Планировщик событий обрабатывает ответ от RIL производителя и вызывает функцию response(Request code) и отправляет ответ RIL Java.
5. RIL Java принимает ответ с помощью метода RILReceiver.run().
Рисунок 6. Запрашиваемая команда.
Незапрашиваемые команды
Существует 11 незапрашиваемых команд, сгрупированных следующим образом:
- изменение сетевого статуса (4);
- входящее SMS сообщение (3);
- входящее USSD уведомление (2);
- изменение силы сигнала или времени (2).
Рисунок 7. Незапрашиваемая команда.
Рисунок 7 иллюстрирует последовательность вызовов на различных уровнях слоя радиоинтерфейса при поступлении незапрашиваемой команды «входящее SMS сообщение»:
1. От модема приходит AT-ответ, RIL производителя считывает его и вызывает соответствующую функцию обработчик. RIL производителя подготавливает данные для отправки их планировщику событий. Заканчивает обработку вызовом OnUnsolicitedResponse().
2. Планировщик событий обрабатывает данные, пришедшие с ответом, отправляет ответ RIL Java через локальный Linux сокет /dev/socket/rild.
3. RIL Java принимает ответ с помощью RILReceiver.run().
В следующей статье я планирую рассказать про то, как написать свою программу для взаимодействия непосредственно с модемом и чего таким образом можно добиться.
При написании статьи использовались:
1. Исходные коды ОС Android android.googlesource.com/
2. dpsm.wordpress.com/2010/09/01/smart-phones-are-still-phones/
3. www.netmite.com/android/mydroid/development/pdk/docs/telephony.html
4. www.slideshare.net/ssusere3af56/android-radio-layer-interface
Автор: stoplinux