Рефакторинг RIL

В Android 7.0 был проведен рефакторинг уровня радиоинтерфейса (RIL) с использованием набора функций для улучшения функциональности RIL. Для реализации этих функций требуются изменения в коде партнеров, которые являются необязательными, но приветствуются. Изменения в рефакторинге обратно совместимы, поэтому предыдущие реализации рефакторизованных функций продолжают работать.

В рамках рефакторинга RIL были внесены следующие улучшения:

  • Коды ошибок RIL. Включает в себя добавление определенных кодов ошибок в дополнение к существующему коду GENERIC_FAILURE . Это помогает в устранении неполадок, предоставляя более конкретную информацию о причине ошибок.
  • Версионирование RIL. Обеспечивает более точную и удобную в настройке информацию о версиях.
  • RIL-связь с использованием блокировок пробуждения. Повышает эффективность работы батареи устройства.

Вы можете реализовать любое из вышеперечисленных улучшений или все сразу. Для получения более подробной информации обратитесь к комментариям к коду, касающимся версионирования RIL, в файле https://android.googlesource.com/platform/hardware/ril/+/android17-release/include/telephony/ril.h .

Внедрить улучшенные коды ошибок RIL.

Практически все вызовы RIL-запросов могут возвращать код ошибки GENERIC_FAILURE в ответ на сообщение об ошибке. Это проблема, характерная для всех запрашиваемых ответов, возвращаемых производителями оборудования, что может затруднить отладку проблемы, описанной в отчете об ошибке, если один и тот же код ошибки GENERIC_FAILURE возвращается вызовами RIL по разным причинам. Производителям может потребоваться значительное время, чтобы даже определить, какая часть кода могла вернуть код GENERIC_FAILURE .

В Android 7.x и выше производители могут возвращать уникальное значение кода ошибки, связанное с каждой отдельной ошибкой, которая в настоящее время классифицируется как GENERIC_FAILURE . Производители, которые не хотят публично раскрывать свои пользовательские коды ошибок, могут возвращать ошибки в виде уникального набора целых чисел (например, от 1 до x), сопоставляемых как OEM_ERROR_1 и OEM_ERROR_X . Производители должны убедиться, что каждый такой замаскированный код ошибки соответствует уникальной причине ошибки в коде. Использование конкретных кодов ошибок может ускорить отладку RIL при возврате производителем общих ошибок, поскольку определение точной причины ошибки GENERIC_FAILURE часто занимает слишком много времени (а иногда это вообще невозможно).

Кроме того, в ril.h добавлены дополнительные коды ошибок для перечислений RIL_LastCallFailCause и RIL_DataCallFailCause , чтобы код поставщика мог избежать возврата общих ошибок, таких как CALL_FAIL_ERROR_UNSPECIFIED и PDP_FAIL_ERROR_UNSPECIFIED .

Проверка кодов ошибок RIL с расширенными возможностями

После добавления новых кодов ошибок для замены кода GENERIC_FAILURE убедитесь, что вызов RIL возвращает новые коды ошибок вместо GENERIC_FAILURE .

Внедрить расширенное версионирование RIL.

В более старых версиях Android версионирование RIL было проблематичным: сама версия была неточной, механизм сообщения версии RIL был неясным (из-за чего некоторые поставщики сообщали неверную версию), а обходной путь для оценки версии был подвержен неточностям.

В Android 7.x и выше файл ril.h содержит все значения версий RIL, описывает соответствующую версию RIL и перечисляет все изменения для этой версии. При внесении изменений, соответствующих версии RIL, разработчики должны обновить свою версию в коде и вернуть эту версию в RIL_REGISTER .

Проверка расширенного версионирования RIL

Убедитесь, что в процессе RIL_REGISTER возвращается версия RIL, соответствующая вашему коду RIL (а не RIL_VERSION определенная в ril.h ).

Реализуйте RIL-связь с использованием блокировок пробуждения (wakelocks).

В RIL-коммуникациях используются блокировки пробуждения по таймеру, что негативно сказывается на расходе заряда батареи. В Android 7.x и выше производительность можно повысить, классифицируя RIL-запросы и обновляя код для обработки блокировок пробуждения по-разному для разных типов запросов.

Классифицировать запросы RIL

Запросы на предоставление информации о состоянии инфраструктуры (RIL) могут быть как запрошенными, так и незапрошенными. Поставщикам следует дополнительно классифицировать запрошенные запросы по одной из следующих категорий:

  • синхронные . Запросы, на которые не требуется значительное время для ответа. Например, RIL_REQUEST_GET_SIM_STATUS .
  • асинхронные запросы. Запросы, на ответ на которые требуется значительное время. Например, RIL_REQUEST_QUERY_AVAILABLE_NETWORKS .

Асинхронные запросы RIL могут занимать значительное время. После получения подтверждения от кода поставщика, RIL Java освобождает блокировку пробуждения, что может привести к переходу обработчика приложения из состояния ожидания в состояние приостановки. Когда от кода поставщика поступает ответ, RIL Java (обработчик приложения) снова получает блокировку пробуждения, обрабатывает ответ, а затем возвращается в состояние ожидания. Такой переход из состояния ожидания в состояние приостановки и обратно может потреблять много энергии.

Если время отклика недостаточно велико, удержание блокировки пробуждения и пребывание в режиме ожидания в течение всего времени, необходимого для ответа, может быть более энергоэффективным, чем переход в состояние приостановки путем снятия блокировки пробуждения и пробуждения при получении ответа. Производителям следует использовать измерения энергопотребления, специфичные для платформы, чтобы определить пороговое значение времени T , при котором энергопотребление при пребывании в режиме ожидания в течение всего времени T превышает энергопотребление при переходе из режима ожидания в режим приостановки и обратно в режим ожидания за то же время T Когда время T известно, команды RIL, выполнение которых занимает больше времени T можно классифицировать как асинхронные, а остальные команды — как синхронные.

Сценарии связи RIL

Приведенные ниже диаграммы иллюстрируют распространенные сценарии обмена данными в рамках протокола RIL и предлагают решения для модификации кода с целью обработки запрашиваемых и незапрашиваемых запросов RIL.

Примечание: Подробную информацию о реализации функций, используемых на следующих диаграммах, см. в методах acquireWakeLock() , decrementWakeLock() и clearWakeLock( ) в ril.cpp .

Сценарий: запрос RIL и запрошенный асинхронный ответ.

В этом сценарии, если ожидается, что ответ от RIL займет значительное время (например, ответ на RIL_REQUEST_GET_AVAILABLE_NETWORKS ), блокировка пробуждения удерживается в течение длительного времени на стороне процессора приложений. Проблемы с модемом также могут привести к длительному ожиданию.

Рисунок 1. Асинхронный ответ, запрошенный RIL.

Решение 1: Модем удерживает блокировку пробуждения для запроса RIL и асинхронного ответа.

Рисунок 2. Блокировка пробуждения, удерживаемая модемом.
  1. Отправляется запрос RIL, и модем получает блокировку пробуждения для обработки этого запроса.
  2. Модем отправляет подтверждение, которое заставляет Java-сервер уменьшить счетчик блокировки пробуждения и освободить его, когда значение счетчика станет равным 0.

    Примечание: Время ожидания блокировки пробуждения для последовательности запрос-подтверждение будет меньше, чем используемое в настоящее время время ожидания, поскольку подтверждение должно быть получено достаточно быстро.

  3. После обработки запроса модем отправляет прерывание коду поставщика, который получает блокировку пробуждения и отправляет ответ в ril.cpp, который, в свою очередь, получает блокировку пробуждения и отправляет ответ на сторону Java.
  4. Когда ответ достигает Java-стороны, устанавливается блокировка пробуждения (wakelock), и вызывающей стороне возвращается ответ.
  5. После обработки ответа всеми модулями подтверждение отправляется (через сокет) обратно в ril.cpp , который затем освобождает блокировку пробуждения, полученную на шаге 3.

Решение 2: Модем не удерживает блокировку пробуждения, и ответ происходит быстро (синхронный запрос и ответ RIL). Синхронное или асинхронное поведение жестко запрограммировано для конкретной команды RIL и определяется для каждого вызова отдельно.

Рисунок 3. Модем не удерживает блокировку пробуждения.
  1. Запрос RIL отправляется путем вызова функции acquireWakeLock() на стороне Java.
  2. Коду поставщика не нужно получать блокировку пробуждения, и он может быстро обработать запрос и ответить.
  3. Когда Java-сервер получает ответ, вызывается метод decrementWakeLock() , который уменьшает счетчик блокировки пробуждения и освобождает блокировку пробуждения, если значение счетчика равно 0.

Сценарий: незапрошенный ответ RIL

В этом сценарии незапрошенные ответы RIL содержат флаг типа wakelock, указывающий, необходимо ли получить блокировку пробуждения для ответа поставщика. Если флаг установлен, устанавливается блокировка пробуждения по таймеру, и ответ отправляется через сокет на сторону Java. По истечении таймера блокировка пробуждения снимается. Время блокировки пробуждения может быть слишком долгим или слишком коротким для разных незапрошенных ответов RIL.

Рисунок 4. Незапрошенная реакция RIL.

Решение: Вместо удержания временной блокировки на стороне Java во время отправки незапрошенного ответа, подтверждение отправляется из Java-кода на нативную сторону ( ril.cpp ).

Рисунок 5. Использование подтверждения (ack) вместо блокировки пробуждения по таймеру (timed wakelock).

Проверить переработанные блокировки пробуждения

Убедитесь, что вызовы RIL идентифицируются как синхронные или асинхронные. Поскольку потребление энергии батареи может зависеть от оборудования/платформы, производителям следует провести внутреннее тестирование, чтобы выяснить, приводит ли использование новой семантики wakelock для асинхронных вызовов к экономии энергии батареи.