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

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

Рефакторинг RIL включает следующие улучшения:

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

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

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

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

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

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

Проверка расширенных кодов ошибок RIL

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

Внедрить улучшенное управление версиями RIL

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

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

Проверить улучшенное управление версиями RIL

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

Реализовать RIL-коммуникацию с использованием wakelocks

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

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

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

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

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

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

Сценарии коммуникации RIL

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

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

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

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

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

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

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

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

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

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

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

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

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

Рисунок 4. Незапрошенный ответ RIL.

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

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

Проверить переработанные wakelock-ы

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