Refatoração do RIL

O Android 7.0 refatorou a camada de interface de rádio (RIL, na sigla em inglês) usando um conjunto de recursos para melhorar a funcionalidade da RIL. As mudanças no código do parceiro são necessárias para implementar esses recursos, que são opcionais, mas recomendados. As mudanças de refatoração são compatíveis com versões anteriores. Portanto, as implementações anteriores dos recursos refatorados continuam funcionando.

A refatoração da RIL inclui as seguintes melhorias:

  • Códigos de erro da RIL. Ativa códigos de erro específicos, além do código GENERIC_FAILURE existente. Isso ajuda na solução de problemas de erros, fornecendo informações mais específicas sobre a causa deles.
  • Controle de versões da RIL. Fornece informações de versão mais precisas e fáceis de configurar.
  • Comunicação da RIL usando wakelocks. Melhora o desempenho da bateria do dispositivo.

É possível implementar qualquer uma ou todas as melhorias acima. Para mais detalhes, consulte os comentários de código sobre o controle de versões da RIL em https://android.googlesource.com/platform/hardware/ril/+/android17-release/include/telephony/ril.h.

Implementar códigos de erro da RIL aprimorados

Quase todas as chamadas de solicitação da RIL podem retornar o código de erro GENERIC_FAILURE em resposta a um erro. Esse é um problema com todas as respostas solicitadas retornadas pelos OEMs, o que pode dificultar a depuração de um problema no relatório de bugs se o mesmo código de erro GENERIC_FAILURE for retornado por chamadas da RIL por motivos diferentes. Pode levar muito tempo para que os fornecedores identifiquem qual parte do código pode ter retornado um código GENERIC_FAILURE.

No Android 7.x e versões mais recentes, os OEMs podem retornar um valor de código de erro distinto associado a cada erro diferente que está categorizado como GENERIC_FAILURE. Os OEMs que não querem revelar publicamente os códigos de erro personalizados podem retornar erros como um conjunto distinto de números inteiros (como 1 a x) mapeados como OEM_ERROR_1 a OEM_ERROR_X. Os fornecedores precisam garantir que cada código de erro mascarado retornado seja mapeado para um motivo de erro exclusivo no código. O uso de códigos de erro específicos pode acelerar a depuração da RIL sempre que erros genéricos forem retornados pelo OEM, já que muitas vezes leva muito tempo para identificar a causa exata de um código de erro GENERIC_FAILURE (e às vezes é impossível descobrir).

Além disso, ril.h adiciona mais códigos de erro para as enumerações RIL_LastCallFailCause e RIL_DataCallFailCause, para que o código do fornecedor possa evitar o retorno de erros genéricos, como CALL_FAIL_ERROR_UNSPECIFIED e PDP_FAIL_ERROR_UNSPECIFIED.

Validar códigos de erro da RIL aprimorados

Depois de adicionar novos códigos de erro para substituir o código GENERIC_FAILURE, verifique se os novos códigos de erro são retornados pela chamada da RIL em vez de GENERIC_FAILURE.

Implementar o controle de versões da RIL aprimorado

O controle de versões da RIL em versões mais antigas do Android era problemático: a versão em si era imprecisa, o mecanismo para informar uma versão da RIL não era claro (fazendo com que alguns fornecedores informassem uma versão incorreta), e a solução alternativa para estimar a versão era propensa a imprecisões.

No Android 7.x e versões mais recentes, ril.h documenta todos os valores de versão da RIL, descreve a versão correspondente e lista todas as mudanças dessa versão. Ao fazer mudanças que correspondam a uma versão da RIL, os fornecedores precisam atualizar a versão no código e retornar essa versão em RIL_REGISTER.

Validar o controle de versões da RIL aprimorado

Verifique se a versão da RIL correspondente ao código da RIL é retornada durante RIL_REGISTER (em vez da RIL_VERSION definida em ril.h).

Implementar a comunicação da RIL usando wakelocks

Os wakelocks cronometrados são usados na comunicação da RIL de maneira imprecisa, o que afeta negativamente o desempenho da bateria. No Android 7.x e versões mais recentes, é possível melhorar o desempenho classificando as solicitações da RIL e atualizando o código para processar os wakelocks de maneira diferente para diferentes tipos de solicitação.

Classificar solicitações da RIL

As solicitações da RIL podem ser solicitadas ou não solicitadas. Os fornecedores precisam classificar ainda mais as solicitações solicitadas como uma das seguintes:

  • Síncronas. Solicitações que não levam muito tempo para responder. Por exemplo, RIL_REQUEST_GET_SIM_STATUS.
  • Assíncronas. Solicitações que levam muito tempo para responder. Por exemplo, RIL_REQUEST_QUERY_AVAILABLE_NETWORKS.

As solicitações assíncronas da RIL podem levar muito tempo. Depois de receber um ACK do código do fornecedor, a RIL Java libera o wakelock, o que pode fazer com que o processador do app passe do estado inativo para o suspenso. Quando a resposta está disponível no código do fornecedor, a RIL Java (o processador do app) readquire o wakelock, processa a resposta e retorna ao estado inativo. Essa mudança do estado inativo para o suspenso e de volta para o inativo pode consumir muita energia.

Se o tempo de resposta não for longo o suficiente, manter o wakelock e permanecer inativo durante todo o tempo necessário para responder pode ser mais eficiente em termos de energia do que entrar no estado suspenso liberando o wakelock e ativando quando a resposta chegar. Os fornecedores precisam usar medições de energia específicas da plataforma para determinar o valor limite do tempo T quando a energia consumida ao permanecer inativo durante todo o tempo T for maior do que a energia consumida ao passar do estado inativo para o suspenso e de volta para o inativo no mesmo tempo T. Quando o tempo T é conhecido, os comandos da RIL que levam mais tempo do que o tempo T podem ser classificados como assíncronos, e os comandos restantes classificados como síncronos.

Cenários de comunicação da RIL

Os diagramas a seguir ilustram cenários comuns de comunicação da RIL e fornecem soluções para modificar o código para processar solicitações solicitadas e não solicitadas da RIL.

Observação:para detalhes de implementação sobre as funções usadas nos diagramas a seguir, consulte os métodos acquireWakeLock(), decrementWakeLock() e clearWakeLock() em ril.cpp.

Cenário: solicitação da RIL e resposta assíncrona solicitada

Nesse cenário, se a resposta solicitada da RIL levar muito tempo (ou seja, uma resposta a RIL_REQUEST_GET_AVAILABLE_NETWORKS), o wakelock será mantido por um longo período no lado do processador do app. Problemas de modem também podem resultar em uma longa espera.

Figura 1. Resposta assíncrona solicitada da RIL.

Solução 1:o modem mantém o wakelock para a solicitação da RIL e a resposta assíncrona.

Figura 2. Wakelock mantido pelo modem.
  1. A solicitação da RIL é enviada, e o modem adquire o wakelock para processar essa solicitação.
  2. O modem envia um reconhecimento que faz com que o lado Java diminua o contador de wakelock e o libere quando o valor do contador for 0.

    Observação:a duração do tempo limite do wakelock para a sequência de solicitação-ACK seria menor do que a duração do tempo limite usada atualmente, porque o ACK precisa ser recebido com bastante rapidez.

  3. Depois de processar a solicitação, o modem envia uma interrupção ao código do fornecedor que adquire o wakelock e envia uma resposta para ril.cpp, que, por sua vez, adquire o wakelock e envia uma resposta para o lado Java.
  4. Quando a resposta chega ao lado Java, o wakelock é adquirido e uma resposta é retornada ao autor da chamada.
  5. Depois que a resposta é processada por todos os módulos, o reconhecimento é enviado (via soquete) de volta para ril.cpp, que libera o wakelock adquirido na etapa 3.

Solução 2:o modem não mantém o wakelock, e a resposta é rápida (solicitação e resposta síncronas da RIL). O comportamento síncrono versus assíncrono é codificado para um comando específico da RIL e decidido em uma base de chamada por chamada.

Figura 3. Wakelock não mantido pelo modem.
  1. A solicitação da RIL é enviada chamando acquireWakeLock() no lado Java.
  2. O código do fornecedor não precisa adquirir o wakelock e pode processar a solicitação e responder rapidamente.
  3. Quando a resposta é recebida pelo lado Java, decrementWakeLock() é chamado, o que diminui o contador de wakelock e libera o wakelock se o valor do contador for 0.

Cenário: resposta não solicitada da RIL

Nesse cenário, as respostas não solicitadas da RIL têm um flag de tipo de wakelock no que indica se um wakelock precisa ser adquirido para a resposta do fornecedor. Se o flag estiver definido, um wakelock cronometrado será definido, e a resposta será enviada por um soquete para o lado Java. Quando o timer expira, o wakelock é liberado. O wakelock cronometrado pode ser muito longo ou muito curto para diferentes respostas não solicitadas da RIL.

Figura 4. Resposta não solicitada da RIL.

Solução:um reconhecimento é enviado do código Java para o lado nativo (ril.cpp) em vez de manter um wakelock cronometrado no lado nativo ao enviar uma resposta não solicitada.

Figura 5. Usar ACK em vez de wakelock cronometrado.

Validar wakelocks redesenhados

Verifique se as chamadas da RIL são identificadas como síncronas ou assíncronas. Como o consumo de energia da bateria pode depender do hardware/plataforma, os fornecedores precisam fazer alguns testes internos para descobrir se o uso da nova semântica de wakelock para chamadas assíncronas leva a economia de energia da bateria.