O Orchestrator é um agente local de SDV que é executado em cada máquina virtual (VM) e fornece um mecanismo para controlar quando os pacotes de serviços devem ser criados, iniciados, interrompidos ou destruídos. Isso é feito por uma configuração de orquestração, em que você define um conjunto de regras que determinam quando e como as ações são executadas em instâncias de pacote de serviços. Essas regras são baseadas nos modos de veículo, energia e personalizado.
É possível configurar o Orchestrator em APEXes de configuração ou por configurações por VM. Esse sistema de configuração distribuída permite que partes de cada pacote de serviços sejam atualizadas de forma independente pelo registro de pacotes de serviços, conforme ilustrado aqui.
Figura 1. Diagrama de configuração do Orchestrator.
As configurações independentes de veículo não mudam de acordo com o OEM ou o veículo. A configuração permanece a mesma em todos os veículos de cada OEM. As configurações específicas do veículo podem ser diferentes em veículos de OEMs diferentes, mas podem ser as mesmas para todos os veículos fabricados por um OEM específico.
APEXes de configuração
No momento da execução, o Orchestrator acessa o registro do pacote de serviços para buscar uma orquestração de SDV para cada pacote de serviços, carregando e analisando cada configuração. Para saber mais, consulte Metadados de orquestração.
Configuração por VM
Quando o Orchestrator é iniciado, ele carrega e analisa a configuração da VM (se
houver). O caminho absoluto para esse arquivo de configuração é especificado pelas propriedades do sistema persist.sdv.orchestrator_config_path e ro.boot.sdv.orchestrator_config_path.
O sistema determina o caminho do arquivo de configuração da VM na inicialização com base na seguinte hierarquia:
O sistema verifica a propriedade
persist.sdv.orchestrator_config_path. Se tiver um valor, esse caminho será usado. Esse valor é mantido após as reinicializações ou definido no ambiente de execução.Se
persist.sdv.orchestrator_config_pathestiver vazio, o sistema vai verificar a propriedadero.boot.sdv.orchestrator_config_path. Se a propriedadero.boot.sdvtiver um valor, esse caminho será copiado para a propriedade persistente e usado para a inicialização atual e todas as futuras (a menos que seja substituído).
persist.sdv.orchestrator_config_path
persist.sdv.orchestrator_config_path é a propriedade principal usada pelo agente do SDV
Orchestrator para acessar o caminho do arquivo de configuração. Essa é uma
propriedade persistente, o que significa que o valor dela é salvo nas reinicializações do dispositivo. É possível
mudar o valor no momento da execução, o que é útil para testes ou cenários específicos
(por exemplo, testes de ponta a ponta).
É possível definir o valor no ambiente de execução usando o comando setprop e, no momento do build,
usando um makefile (com a extensão .mk) ou um arquivo de script de recurso, com a
extensão .rc.
Definir a propriedade no momento da execução
Definir a propriedade no momento da execução é útil para testar ou fazer mudanças temporárias, já que o valor é salvo entre as reinicializações:
adb root
adb shell setprop persist.sdv.orchestrator_config_path {$path_to_file}.textproto
Definir a propriedade no momento do build
Para definir essa propriedade como parte da configuração do build do dispositivo, adicione uma linha ao makefile do produto ou da placa. Isso é ideal para definir um valor padrão para uma nova imagem de dispositivo.
# Add this line to a product's or device's .mk file
PRODUCT_PROPERTY_OVERRIDES += persist.sdv.orchestrator_config_path={$path_to_file}.textproto
Também é possível definir essa propriedade em um arquivo de script de recurso:
# Add this line to an .rc file
on {$property}
setprop persist.sdv.orchestrator_config_path {$path_to_file}.textproto
ro.boot.sdv.orchestrator_config_path
ro.boot.sdv.orchestrator_config_path é uma propriedade somente leitura de tempo de inicialização usada para
fornecer um valor inicial para a propriedade
persist.sdv.orchestrator_config_path. Se persist.sdv.orchestrator_config_path estiver vazio quando o sistema
for inicializado, o valor de ro.boot.sdv.orchestrator_config_path será copiado para ele. Depois que
persist.sdv.orchestrator_config_path for definido, ele não será substituído por
essa propriedade nas inicializações seguintes.
É possível definir ro.boot.sdv.orchestrator_config_path usando o bootconfig ou a
linha de comando do kernel.
Formato do arquivo
Defina a configuração de orquestração em um formato .textproto (por exemplo, em texto estruturado) para que novas configurações possam ser carregadas no ambiente de execução.
Sintaxe de configuração
Nesta seção, descrevemos a sintaxe de configuração.
Pacotes de serviços
Cada pacote de serviços precisa ser definido com a configuração do pacote de serviços, que identifica o pacote no Orchestrator e é usada para processar o ciclo de vida da instância do pacote. A configuração do pacote de serviços define:
O
InstanceToGroupMappingpermite incluir uma instância do pacote de serviços em um grupo para estabelecer dependências entre instâncias do mesmo pacote de serviços.InstancesStatesdefine os diferentes estados da instância do pacote de serviços.InstancesStateConfigurationdefine o estado (deInstancesStates) que uma instância de pacote de serviço precisa ter se a condição for avaliada comotrue.ServiceBundleConfigcontém informações sobre o pacote de serviços específico e as instâncias dele. Ele contém, para o pacote, os respectivosInstanceToGroupMappingeInstancesStateConfiguration.CustomModesdefine uma lista de modos personalizados que o pacote pode publicar.CustomModesé usado para impedir que um pacote não autorizado modifique o valor de um modo personalizado. Esse campo é opcional, já que o pacote de serviços pode não ser publicado em nenhum modo personalizado. Para saber mais, consulte Modos personalizados.
Configuração de pacote necessária
No mínimo, a configuração do pacote fornece estes atributos:
service_bundle_config {
package_name: "package_name"
service_bundle_name: "service_bundle_name"
instance: "instance_1"
instance: "instance_n"
}
Essa declaração define instâncias de pacote de serviços n com os respectivos FQINs:
vm_name.package_name.service_bundle_name.instance_1
…
vm_name.package_name.service_bundle_name.instance_n
O nome da VM não é declarado explicitamente na configuração. Como a configuração é definida por VM, o nome da VM é sempre o nome da VM em que o arquivo de configuração é implantado e já é conhecido pelo agente de orquestração.
Configurar instâncias
A declaração de instâncias de pacote de serviços não tem efeito. Para serem executadas pelo Orchestrator, as instâncias precisam ser configuradas. Por exemplo, o Orchestrator precisa ser informado sobre quais condições (ou estado da VM ou do veículo) uma instância deve ser executada. Para configurar instâncias, os estados de configuração precisam ser definidos por:
conditioné uma expressão no estado da VM ou do veículo que precisa ser avaliada.instances_statesé um conjunto de estados por instância que precisa ser aplicado se a condição for avaliada comotrue.
Para saber mais, consulte Condições.
service_bundle_config {
package_name: "oem.package"
service_bundle_name: "OemApplication"
instance: "adaptive_light"
instance: "reserve_light"
state {
condition {
power_state: "ON"
}
instances_states {
started: "adaptive_light"
created: "reserve_light"
}
}
}
Mapeamento de instância para grupo
Também é possível configurar instâncias de serviço incluindo-as em grupos de serviços. No nível de configuração do pacote de serviços, é possível adicionar instâncias a grupos. Em seguida, é possível configurar grupos em um nível de configuração de VM. Para saber mais, consulte a próxima seção e Pacotes de serviços.
service_bundle_config {
package_name: "oem.package"
service_bundle_name: "OemApplication"
instance: "fog_front_light"
instance: "fog_rear_light"
instance: "turn_signal_light"
instance: "light_flasher_display"
# Declare that fog_light contains fog_front_light and fog_rear_light.
group_mapping {
group: "fog_light"
instance: "fog_front_light"
instance: "fog_rear_light"
}
# Declare that flasher_light contains turn_signal_light and light_flasher_display.
group_mapping {
group: "flasher_light"
instance: "turn_signal_light"
instance: "light_flasher_display"
}
}
Esquema proto
Veja um exemplo de esquema de proto:
// Service bundle configuration.
//
// Defines service bundle data, its instances and configuration for instances
// states depending on the state of the system
message ServiceBundleConfig {
// Required. Name of the service bundle.
string service_bundle_name = 1;
// Required. Package name of the service bundle.
string package_name = 2;
// Required. Service instances.
repeated string instance = 3;
// Configuration for instances states depending on the state of the system.
repeated InstancesStateConfiguration state = 4;
// Mapping of groups to their member service instances.
repeated InstanceToGroupMapping group_mapping = 5;
// Custom modes that this service bundle is allowed to set.
repeated string custom_mode = 6;
// Defines the retry policies for specific instances.
// If multiple mappings target the same instance, the one with the highest `max_retries`
// value takes precedence. This applies across all configuration files.
repeated InstanceToRetryMapping retry_mapping = 7;
}
// Mapping of instances to their retry configuration.
message InstanceToRetryMapping {
// Required.
//
// Name of the instances for which the given retry configuration is applied.
repeated string instance = 1;
// Required.
//
// The configuration that defines the restart and retry strategy for the instances.
RetryConfiguration retry_config = 2;
// Configuration for retry and restart.
// This configuration is applied after a failure on a transition or after the bundle instance
// has crashed. Upon a successful operation, the retry counter are reset to max_retries. This
// configuration can be applied to any service bundle, not only the monitored ones. If the
// configuration is not provided or none of the optional fields are filled, the default behavior
// stated is applied (the value from `ro.boot.sdv.orchestrator.recovery.max_retries`
// or zero if not set).
message RetryConfiguration {
// The number of times a retry/restart operation can be performed.
// Defines the number of times the Orchestrator retries a transition
// after a transient failure or after a bundle crash notification.
// This applies to creating, starting and destroying operations.
// The retry count resets to max_retries after a successful operation.
// If not set, the default configured value in the
// `ro.boot.sdv.orchestrator.recovery.max_retries` is used, or-if not set-
// it fallbacks to zero.
optional uint32 max_retries = 1;
}
}
// Mapping of groups to their member service instances.
message InstanceToGroupMapping {
// Required. Names of groups to which members are added.
//
// Group behavior is defined in VM configuration.
repeated string group = 1;
// Required. Names of instances to be included in the groups.
//
// Can reference only instance defined in the same config file.
repeated string instance = 2;
}
// Describes the state the service instances should be in after the state is executed.
//
// If there is no valid configuration for the instance in the specific system state, such instance is transitioned to the "destroyed" state.
//
// For the GroupsStates definition to be useful, at least one item should be present in any of the fields.
message InstancesStates {
// Names of the instances that must be in a "created" state.
repeated string created = 1;
// Names of the instances that must be in a "started" state, overrides "created" state.
repeated string started = 2;
// Names of the instances that must not run, overrides all other states.
repeated string destroyed = 3;
}
// Configuration for instances states depending on the state of the system.
message InstancesStateConfiguration {
// Condition for the system state under which the related instances states should be executed by Orchestrator.
//
// If omitted, the related instances states are always executed.
Condition condition = 1;
// Required. States of service bundle instances to be executed by Orchestrator if the condition is true.
InstancesStates instances_states = 2;
}
Configuração no nível da VM
A configuração da VM permite definir mapeamentos e configurações de grupos. Ele é usado para modelar dependências entre pacotes de serviços no nível da VM, o que dá flexibilidade para modificar o estado de vários pacotes de serviços ao mesmo tempo. Todas as instâncias de um grupo são levadas para o estado especificado.
O Orchestrator não garante a ordem em que a mudança de estado é executada. O Orchestrator promove cada instância para que ela esteja no estado especificado.
Os grupos são declarados implicitamente usando o nome group em qualquer uma das
partes da configuração. Por exemplo, mapeamento de instância para grupo, mapeamento de grupo para grupo e estados de configuração de grupo.
Mapeamento de grupo para grupo
Os grupos podem conter outros grupos. Ao declarar que group_1 contém subgroup_2, adicionamos todas as instâncias de serviço de subgroup_2 a group_1.
Exemplo:
# Declare that body contains fog_light and flasher_light.
group_mapping {
group: "body"
subgroup: "fog_light"
subgroup: "flasher_light"
}
Configurar um grupo
Declarar um grupo não tem efeito. Para serem executados pelo Orchestrator, os grupos precisam ser configurados. Por exemplo, o Orchestrator precisa ser informado sobre as condições ou o estado da VM ou do veículo em que os grupos devem ser executados.
É possível configurar grupos de configuração de maneira semelhante às instâncias de serviço, ou seja, usando estados de configuração. A única diferença é o uso de groups_states
em vez de instances_states na sintaxe:
state {
condition {
power_state: "ON"
}
groups_states {
started: "Body"
started: "Adas"
}
}
Esquema proto
Veja um exemplo de esquema de proto:
// VM configuration.
//
// Defines group-to-group mappings and configuration for groups
// states depending on the state of the system.
//
// Configurations of service bundles can also be defined in VM configuration (as well as in a separate configuration file).
message VmConfig {
// Group to member groups mapping.
repeated GroupToGroupMapping group_mapping = 1;
// Configuration of group states.
repeated GroupsStateConfiguration state = 2;
// Required. We also allow to configure individual service bundles in the VM config, to simplify development and migration from the monolithic configuration.
repeated ServiceBundleConfig service_bundle_config = 3;
}
// Mapping of groups to their member groups.
message GroupToGroupMapping {
// Required. Names of groups to which members are added.
repeated string group = 1;
// Required. Names of member groups to be included in the groups.
repeated string subgroup = 2;
}
// Describes the state the service instance groups should be in after the state is executed.
//
// If group configuration is valid in a specific system state, the configured state is applied to
// all group members. After that, the normal service instance configuration rules still apply:
// - "destroyed" > "started" > "created" precedence
// - not configured means the instance should be moved to the default state
//
// For the GroupsStates definition to be useful, at least one item should be present in any of the fields.
message GroupsStates {
// Names of the groups that must be in a "created" state.
repeated string created = 1;
// Names of the groups that must be in a "started" state, overrides "created" state.
repeated string started = 2;
// Names of the groups that must not run, overrides all other states.
repeated string destroyed = 3;
}
// Configuration for group states depending on the state of the system.
message GroupsStateConfiguration {
// Condition for the system state under which the related group states should be executed by Orchestrator.
//
// If omitted, the related groups states are always executed.
Condition condition = 1;
// Required. States of service bundle groups to be executed by Orchestrator if the condition is true.
GroupsStates groups_states = 2;
}
Estados de configuração
Os estados de configuração (estados) definem quando uma instância ou um grupo de serviços é
iniciado, interrompido ou destruído e consistem em conditions e
instances_states (configuração do pacote) e groups_states (configuração da VM).
Condições
As condições permitem que o modelo tenha uma condição booleana em que, quando avaliada como
true, o estado da instância definido é aplicado. Uma condição tem estas
características:
Expressão booleana arbitrariamente complexa (formada com expressões
andounot) com base em indicadores compatíveis, como energia, veículo e modo personalizado.(Opcional) O estado de configuração sem uma condição está sempre ativo, o que significa que ele é avaliado como
true.
Estados de instância e de grupo
instances_states e groups_states têm essas características.
Determinar quais estados são necessários para o agente de orquestração aplicar às instâncias ou aos grupos, considerando que o estado é
active.Ao aplicar um estado ao grupo, ele é aplicado a cada instância do pacote de serviços no grupo. Nenhuma ordem é aplicada quando as instâncias são trazidas para o estado.
Os estados aceitos incluem:
starteddepois queService::on_startfor chamado.createdapós a chamada de
Service::new, mas antes da chamada deService::on_start.OU
após a chamada de
Service::on_stop, mas antes da chamada deService::drop.
destroyeddepois queService::dropfor chamado.
Conjunto de regras
Um estado de configuração pode ser ativo ou inativo, dependendo da condição. Vários estados podem estar ativos ao mesmo tempo. Quando um agente de orquestração recebe uma atualização de sinal, todos os estados de configuração são avaliados antes de modificar o ciclo de vida dos pacotes de serviços. Os estados da instância de serviço são avaliados de acordo com estas regras:
Quando nenhum dos estados ativos se aplica à instância de serviço, ela é destruída.
Quando um ou mais estados ativos se aplicam, esta precedência é válida:
destroyedtem precedência absoluta.startedtem precedência sobrecreated.
Esquema proto
Veja um exemplo de esquema de proto:
// A root boolean condition.
message Condition {
// Required.
oneof root {
// VPM power state condition.
string power_state = 1;
// VPM vehicle state condition.
string vehicle_state = 2;
// Custom mode state condition.
CustomState custom_state = 3;
// Negation of a nested condition.
Condition not = 4;
// Logical 'and' between conditions grouped in expression.
Expression and = 5;
// Logical 'or' between conditions grouped in expression.
Expression or = 6;
}
}
// Representation of Custom state condition.
//
// Custom mode(s) are defined by the OEM and are not standardized by the platform, in contrast with
// VPM modes (i.e. power and vehicle mode).
message CustomState {
// Custom mode being checked.
string mode = 1;
// State of the custom mode.
string state = 2;
}
// A set of conditions united under an 'and' or 'or' expression.
//
// Evaluation type ('and' or 'or') depends on the field in [Condition]/[Expression], where the
// expression is being used.
//
// At least one value in at least one of the fields is required.
message Expression {
// VPM power state condition.
repeated string power_state = 1;
// VPM vehicle state condition.
repeated string vehicle_state = 2;
// Custom mode state condition.
repeated CustomState custom_state = 3;
// Negation of a nested condition.
repeated Condition not = 4;
// Logical 'and' between conditions grouped in expression.
repeated Expression and = 5;
// Logical 'or' between conditions grouped in expression.
repeated Expression or = 6;
}
Estratégia de recuperação de falhas e reinicialização
O Orchestrator oferece um mecanismo robusto para lidar com falhas de pacotes de serviços e de transição do ciclo de vida. Como o Orchestrator tem uma visão holística dos estados do serviço e gerencia as transições de modo, ele é o componente mais adequado para executar a estratégia de reinicialização e nova tentativa. O Lifecycle Manager (LM) informa falhas no pacote de serviços ao Orchestrator por notificações de encerramento do Binder. Para evitar chamadas de binder desnecessárias para o LM, o Orchestrator armazena em cache o último estado de cada pacote (seja bem-sucedido ou não) e não reaplica transições se o último estado conhecido for igual ao novo estado solicitado.
Tentar novamente a configuração
É possível definir a estratégia de reinicialização e nova tentativa por instância na configuração do Orchestrator
usando retry_mapping. Se max_retries não estiver definido na
configuração, o valor padrão será extraído da
propriedade do sistema ro.boot.sdv.orchestrator.recovery.max_retries. Se essa
propriedade não for definida, o valor será revertido para 0.
max_retries: define o número de vezes que o Orchestrator tenta uma transição novamente após uma falha temporária ou uma notificação de falha do pacote. O contador de novas tentativas é redefinido para o valor demax_retriesapós uma operação bem-sucedida ou quando um novo modo é processado. Se vários mapeamentos segmentarem a mesma instância, aquele com omax_retriesmais alto terá precedência.
Exemplo de configuração
service_bundle_config {
package_name: "oem.package"
service_bundle_name: "OemApplication"
instance: "fog_front_light"
instance: "fog_rear_light"
# Defines the restart configuration mapping for specific instances.
retry_mapping {
instance: "fog_front_light"
instance: "fog_rear_light"
retry_config {
max_retries: 3
}
}
}
Comportamento de recuperação
A lógica de reinicialização e nova tentativa do Orchestrator gerencia de forma adequada vários cenários de falha:
- Falha na operação normal: se um pacote de serviços falhar durante a execução, o Orchestrator vai aplicar a estratégia de reinicialização e tentar retornar o pacote ao último estado solicitado com base nas novas tentativas restantes.
- Falha durante a transição de modo: se um pacote falhar ao aplicar um novo modo, a solicitação de reinicialização será colocada na fila e processada mais tarde. Depois que a solicitação é processada, o Orchestrator verifica qual foi o último estado da instância e só aplica a reinicialização se a instância não estiver no último estado solicitado (da última transição de modo).
- Novo modo durante a recuperação: se o Orchestrator receber uma solicitação para fazer a transição para um novo modo enquanto um pacote estiver sendo reiniciado (ou estiver na fila de instâncias para reiniciar), ele vai cancelar a recuperação em andamento. A nova transição de modo assume o controle, e o contador de novas tentativas é redefinido para permitir um novo conjunto de tentativas para o novo estado de destino.
O Orchestrator distingue entre diferentes tipos de erro retornados pelo Lifecycle Manager para determinar a estratégia de repetição:
- Erros temporários (
SERVICE_NOT_FOUND,OPERATION_FAILED,INTERNAL_ERROR): o Orchestrator tenta realizar a operação novamente sem realizar nenhuma ação de limpeza especial. - Erros persistentes (
VALUE_CORRUPTED,INVALID_ARGUMENT): o Orchestrator assume que o pacote de serviços pode estar em um estado corrompido e tenta encerrar a instância de serviço antes de repetir a operação para garantir uma reinicialização limpa. - Erros permanentes (
PERMISSION_DENIED): a operação não é repetida, e o pacote é considerado em um estado irrecuperável.
Se o Lifecycle Manager falhar, todos os processos do pacote de serviços serão perdidos. Como o estado real é desconhecido, o Orchestrator invalida cada instância e aplica uma estratégia de reinicialização com as novas tentativas restantes para colocar cada instância no último estado solicitado.
Para evitar loops de recuperação infinitos para pacotes que falham repetidamente,
o contador de novas tentativas só é redefinido para max_retries após uma operação de ciclo de vida
bem-sucedida ou quando uma nova transição de modo é solicitada. Se um pacote esgotar as
tentativas devido a falhas consecutivas (por exemplo, uma falha de transição seguida
por uma falha), ele não será reiniciado até que o contador de novas tentativas seja redefinido.
Relatórios de estado para o monitor de integridade
O Orchestrator expõe uma interface de binder interna em que o Health Monitor (HM) se registra, permitindo que ele receba atualizações contínuas sobre o estado de todos os pacotes de serviços. Por essa interface, o Orchestrator informa ativamente:
- Estado do ciclo de vida: o estado pretendido da instância com base na configuração atual e nos modos ativos (como iniciado, criado ou destruído).
- Estado de recuperação: o status de alcance do estado do ciclo de vida pretendido, indicando se a instância está operacional, se está tentando novamente após uma falha ou se não conseguiu se recuperar após esgotar todas as tentativas.
Essas informações são informadas para todas as instâncias, incluindo aquelas que não se registraram para serem monitoradas por heartbeat. O HM usa essas informações para implementar as APIs que informam o estado da VM. Saiba mais em Monitoramento de integridade.
Exemplos
Nesta seção, apresentamos exemplos de como configurar estados com condições.
Exemplo de serviço básico
- Não tem condição e, portanto, está sempre ativa.
- Inicia uma única instância de serviço.
state {
# Note: This state has no condition, hence its actions are valid throughout the lifetime of the program
instances_states { started: "ServiceBundleName" }
}
Exemplo de app de AVAC
Condição: ativo quando
custom_state != occupancy.OCCUPANCY_EMPTY || custom_state == preheat.PREHEAT_ON.Declara várias instâncias de serviço relacionadas ao HVAC como
started.
state {
condition {
or {
# I.e. when the vehicle is occupied (for example, by _DRIVER / _NON_DRIVER / _PET)
not {
custom_state {
mode: "occupancy"
state: "OCCUPANCY_EMPTY"
}
}
custom_state {
mode: "preheat"
state: "PREHEAT_ON"
}
}
}
# HVAC-related services
instances_states {
started: "HvacTemperatureCommand"
started: "TempSensorDriverZone"
started: "TempSensorPassengerZone"
started: "RefrigerantLoop"
}
}
Exemplo de economia de energia
Condição: ativo quando
custom_state == system_power.SYSTEM_POWER_LOW && custom_state == range_ext.RANGE_EXT_ON.Esse estado pode ser visto como um estado de economia de energia.
Declara uma instância de serviço relacionada ao AVAC como
destroyed.Neste exemplo, quando os modos
SYSTEM_POWER_LOWeRANGE_EXT_ONestão ativos, o app HVAC é executado sem a instância de serviçoRefrigerantLoop:state { condition { and { custom_state { mode: "system_power" state: "SYSTEM_POWER_LOW" } custom_state { mode: "range_ext" state: "RANGE_EXT_ON" } } } # Disable services with high power consumption instances_states { destroyed: "RefrigerantLoop" } }
Exemplo de vida a bordo
Condição: ativo se
power_state == ON && vehicle_state == LIFE_ON_BOARD.Esse estado pode ser visto como Alguém está no carro e o carro está ligado.
Declara que os sensores de temperatura estão em execução.
Quando alguém está no carro, itens como a temperatura são monitorados por motivos de segurança.
state {
condition {
and {
power_state: "ON"
vehicle_state: "LIFE_ON_BOARD"
}
}
# Temperature monitoring services
instances_states {
started: "TempSensorDriverZone"
started: "TempSensorPassengerZone"
}
}
Exemplos
Esta seção apresenta exemplos completos que contêm:
Uma configuração de proto no nível do pacote de serviços que introduz um pacote de serviços com duas instâncias, cada uma sendo parte de um grupo
Uma configuração de proto no nível da VM que introduz lógica para interagir com os grupos com base em modos
Configuração no nível do pacote de serviços
# proto-file: //system/software_defined_vehicle/orchestration/distributed_config/src/protos/service_bundle_config.proto
# proto-message: ServiceBundleConfig
package_name: "oem.package"
service_bundle_name: "OemApplication"
instance: "fog_front_light"
instance: "fog_rear_light"
instance: "turn_signal_light"
custom_mode: "FOG"
custom_mode: "TURN"
group_mapping {
group: "fog_light"
instance: "fog_front_light"
instance: "fog_rear_light"
}
group_mapping {
group: "flasher_light"
instance: "turn_signal_light"
}
state {
condition {
power_state: "ON"
}
instances_states {
created: "turn_signal_light"
destroyed: "fog_front_light"
destroyed: "fog_rear_light"
}
}
Configuração no nível da VM
# proto-file: //system/software_defined_vehicle/orchestration/distributed_config/src/protos/vm_config.proto
# proto-message: VmConfig
group_mapping {
group: "lights"
subgroup: "fog_light"
subgroup: "flasher_light"
}
state {
condition {
custom_state {
mode: "FOG"
state: "ON"
}
}
groups_states {
started: "fog_light"
}
}
state {
condition {
custom_state {
mode: "TURN"
state: "RIGHT"
}
}
groups_states {
started: "flasher_light"
}
}
state {
condition {
vehicle_state: "SUSPEND_TO_RAM_ENTER"
}
groups_states {
created: "lights"
}
}
Configurar o paralelismo de gerenciamento de pacotes
A propriedade do sistema ro.boot.sdv.max_bundles_management_threads é um parâmetro de ajuste importante para controlar o desempenho e o consumo de recursos durante as operações do ciclo de vida do pacote de serviços. Ele define o nível máximo de paralelismo
para transações de pacotes de serviços e afeta diretamente dois serviços principais:
Mecanismo de orquestração: esse serviço lê a propriedade para determinar quantas chamadas simultâneas (por exemplo,
startService,stopService) o Orchestrator pode fazer ao Lifecycle Manager. Isso é crucial para o desempenho durante a inicialização e as transições de modo, em que muitos pacotes podem mudar de estado simultaneamente.Lifecycle Manager: esse serviço usa o valor da propriedade para calcular o tamanho do pool de linhas de execução do Binder, que é responsável por processar todas as solicitações recebidas. Isso garante que o LM tenha linhas de execução suficientes para processar as solicitações simultâneas do Orchestrator.
Se essa propriedade não for definida, os dois serviços vão usar o valor padrão 12.
Método de configuração
Para definir a propriedade no arquivo BoardConfig.mk do dispositivo, adicione-a à
variável BOARD_BOOTCONFIG. Isso garante que o valor seja aplicado sempre que
o dispositivo for inicializado.
BOARD_BOOTCONFIG += \
androidboot.sdv.max_bundles_management_threads=8
Para mudar o valor do seu dispositivo, modifique essa linha no arquivo BoardConfig.mk
adequado e recrie.
Otimização do tempo de inicialização
O ro.sdv.orchestrator.state.ready é uma propriedade booleana write-once que faz
parte de uma estratégia de otimização de desempenho no momento da inicialização. Isso indica que o
agente de orquestração concluiu a inicialização e está pronto para começar
a gerenciar o ciclo de vida dos pacotes de serviços. A principal finalidade dele é priorizar
a inicialização do Orchestrator e dos pacotes de serviços gerenciados, controlando
a sequência de inicialização de outros agentes de SDV.
- Definido por: o agente de orquestração.
- Quando: uma vez durante a sequência de inicialização.
- Uso: essa propriedade é usada pelo sistema de inicialização para controlar a sequência de inicialização da maioria dos agentes de SDV (gerenciador de atualizações, provedor de VSIDL, monitor de integridade, descoberta de serviços, túnel de dados, RPC, VPM e telemetria). Ao iniciar o Orchestrator com antecedência e fazer com que outros agentes aguardem essa propriedade, o sistema garante que o Orchestrator possa iniciar a tarefa crítica de iniciar pacotes de serviços sem competir por recursos do sistema.
Desempenho
Durante a inicialização do sistema, iniciar todos os agentes simultaneamente pode levar à contenção de recursos, desacelerando o processo geral de inicialização. Para mitigar isso, uma ordem de inicialização sequencial é aplicada usando propriedades do sistema:
- Registro de pacotes de serviços: inicia primeiro para carregar todos os metadados de pacotes de serviços.
- Gerenciador de ciclo de vida e orquestrador: esses agentes principais são iniciados assim que o registro está pronto. Esse início antecipado é crucial, porque permite que o Orchestrator comece a avaliar a configuração e se prepare para iniciar os pacotes de serviços imediatamente.
- Outros agentes de SDV: começam apenas depois que o Orchestrator está pronto.
Essa sequência controlada garante que o Orchestrator tenha prioridade para usar os recursos do sistema para iniciar os pacotes de serviços o mais rápido possível, levando a uma inicialização do sistema mais rápida, determinística e eficiente.
Modos consumidos pelo Orchestrator
O agente de orquestração mantém uma assinatura ativa para os modos de veículo e energia
transmitidos pelo VPM. Após o estabelecimento da conexão inicial entre o
Orchestrator e o sistema de gerenciamento de veículos e energia (VPM, na sigla em inglês), o Orchestrator
define as seguintes propriedades booleanas do sistema como true:
sdv.orchestrator.bootup.power_mode.readysdv.orchestrator.bootup.vehicle_mode.ready
Usando um arquivo de configuração como referência, o agente de orquestração calcula dinamicamente o conjunto de pacotes de serviços que precisam estar em um estado de execução com base nos valores atuais dos modos recebidos. Em seguida, o Orchestrator se comunica com o gerenciador de ciclo de vida, emitindo comandos diferentes para alinhar o estado real dos pacotes de serviços com o estado de destino calculado.
Estados do veículo e de energia
O agente de modo do veículo e gerenciamento de energia (VPM, na sigla em inglês) permite que os componentes do SDV sejam informados sobre o estado atual do veículo, como o modo operacional (por exemplo, em estacionamento ou direção) e o status de energia (por exemplo, ligado e suspenso). O Orchestrator avalia esses valores para definir quais pacotes de serviços devem ser executados com base na configuração do Orchestrator. Para saber mais, consulte Gerenciamento de veículos e energia.
Modos personalizados
Como existem muitos, não podemos modelar todos os modos de veículo. Cada OEM tem necessidades diferentes, e a padronização dos modos de veículo não pode atender a todos os casos de uso de OEM. Por isso, oferecemos suporte a modos específicos de OEM, conhecidos como modos personalizados. Esses modos não estendem os modos de veículo e de energia atuais. Em vez disso, eles oferecem uma maneira de definir novos modos.
Funcionalidade:
Escopo global: os modos personalizados são globais e se aplicam de maneira uniforme a todas as VMs gerenciadas pelo Orchestrator.
Composição: cada mudança de modo personalizado consiste em dois elementos:
Nome: identificador exclusivo selecionado pelo OEM para representar o modo personalizado.
Valor: o estado atual do modo personalizado, que pode ser
UNDEFINEDquando nenhum valor é definido.
Papel de orquestrador: o orquestrador atua como um receptor passivo de valores de modo personalizado.
Papel do pacote de serviços: cada pacote de serviços pode ter vários modos personalizados e publicar novos valores para qualquer um deles. Vários pacotes de serviços podem ter o mesmo modo personalizado, o que significa que um modo personalizado pode receber novos valores de fontes diferentes.
Responsabilidade de validação: os OEMs são responsáveis por garantir transições de estado válidas. O Orchestrator aceita qualquer valor novo.
Características estimadas:
Contagem estimada: os modos de energia e de veículo gerenciam o ciclo de vida da maioria dos pacotes de serviços, e os modos personalizados desempenham um papel complementar. Esperamos que a ordem de magnitude dos modos personalizados seja de dezenas, e não de centenas.
Tempo estimado: os modos não são enviados periodicamente. Em vez disso, os modos são orientados por eventos, acionados por ações específicas, como abrir uma porta, iniciar uma sequência de estacionamento, iniciar um ciclo de carga e outros eventos semelhantes de importância definida pelo OEM.
O design de modo personalizado oferece flexibilidade para definir e gerenciar modos específicos, permitindo que o Orchestrator permaneça agnóstico em relação à lógica da máquina de estado subjacente.
Modos compatíveis
Para garantir que os pacotes de serviços publiquem apenas nos modos personalizados que possuem, cada um precisa declarar explicitamente a lista de modos personalizados que possui na configuração do Orchestrator, que está disponível na VM local com o registro do pacote de serviços. As tentativas de publicação em um modo personalizado não declarado são descartadas.
Para declarar os modos personalizados que um pacote de serviços pode publicar (e, portanto,
é proprietário), o esquema proto service_bundle_config é estendido com o seguinte:
// Service bundle configuration.
//
// Defines service bundle metadata, its instances, and configuration for instances
// lifecycle states depending on the state of the vehicle.
message ServiceBundleConfig {
[...]
// The list of custom modes that this service bundle publishes.
repeated string custom_mode = 5;
}
Configurar pacotes com base em modos
A configuração proto do Orchestrator (o componente Condition)
permite a manipulação de pacotes de serviços com base em modos personalizados:
message Condition {
oneof root {
string power_state = 1;
string vehicle_state = 2;
CustomState custom_state = 3;
Condition not = 4;
Expression and = 5;
Expression or = 6;
}
}
message CustomState {
// Custom mode being checked.
string mode = 1;
// State of the custom mode.
string state = 2;
}
Exemplo de proto
O exemplo a seguir mostra como configurar instâncias de um pacote de serviços para serem
iniciadas com base em um estado TURN e FOG:
service_bundle_config {
package_name: "oem.package"
service_bundle_name: "OemApplication"
instance: "flasher_light"
// Service bundle is allowed to set values for the TURN mode
custom_mode: "TURN"
// Service bundle is allowed to set values for the FOG mode
custom_mode: "FOG"
state {
condition {
custom_state {
mode: "TURN"
state: "LEFT"
}
}
instances_states {
started: "flasher_light"
}
}
state {
condition {
custom_state {
mode: "FOG"
state: "ON"
}
}
// Group assumed to be defined containing all FOG lights instances.
groups_states {
started: "fog_lights"
}
}
}
Definir novos modos personalizados
O processo de definição de um novo valor de modo personalizado começa com o pacote de serviços,
que comunica o valor desejado ao Orchestrator local em execução na
mesma VM. Em seguida, o Orchestrator verifica se o pacote de serviços tem as
permissões necessárias para publicar no modo personalizado especificado, referenciando a
configuração definida no .textproto, para determinar se ele precisa propagar
o valor para outras VMs. Depois de propagado, cada Orchestrator analisa a
configuração para encontrar a lista de pacotes de serviços em que o estado precisa ser
alterado.
RPC
Cada Orchestrator em execução em cada VM cria um servidor RPC para detectar novos valores de modo personalizado. Cada pacote de serviços que quiser atualizar um modo personalizado precisa criar um cliente RPC para o servidor. As ACLs são aplicadas para impedir que pacotes não autorizados se conectem ao servidor.
A definição de proto para definir um novo valor por RPC é semelhante a este exemplo:
syntax = "proto3";
import "google/protobuf/timestamp.proto";
package com.sdv.google.Orchestrator;
// Representation of the request used by service bundles to update a custom mode.
// Service bundles are permitted to update only the custom modes specifically designated
// for them within the Orchestrator configuration.
message SetCustomStateRequest {
// Required.
// The name of the custom mode.
// The mode string can not be longer than 56 characters and can only contain
// letters, numbers, dashes, dots and underscores: [a-zA-Z0-9_-.].
// No other special character nor spaces should be present in the mode.
string mode = 1;
// Required.
// The new value for the custom mode.
// The value string can not be longer than 56 characters and can only contain
// letters, numbers, dashes, dots and underscores: [a-zA-Z0-9_-.].
// No other special character nor spaces should be present in the value.
string value = 2;
// Required.
// The timestamp in which the new custom mode value was set. This is used to
// prevent race conditions whenever different service bundles in different
// VMs want to set a new value for the same custom mode.
// We use this timestamp to order the requests and we promise eventual
// consistency: while temporary inconsistencies may occur, the system will
// eventually converges to the correct state.
.google.protobuf.Timestamp timestamp = 3;
}
// Representation of the set custom state response.
message SetCustomStateResponse {}
// Orchestrator interface for service bundles that update the value of a
// custom mode.
// When a new value is received, it is propagated to Orchestrators running on
// other VMs.
service CustomStateService {
// Updates the value for the custom mode.
// Returns the error:
// - PermissionDenied: the service is not authorized to update the custom mode.
// - InvalidArgument: the provided mode and/or value are not valid.
rpc SetCustomState(SetCustomStateRequest) returns (SetCustomStateResponse) {};
}
Cancelamento de transições de energia
O Orchestrator permite o cancelamento de transições de energia em andamento pelo
modo de energia SHUTDOWN_CANCELLED (enviado pelo VPM ao Orchestrator).
Considere a seguinte configuração do Orchestrator como exemplo:
state {
condition {
power_state: "SUSPEND_TO_RAM_ENTER"
}
instances_states {
started: "instance-1"
started: "instance-2"
started: "instance-3"
}
}
Quando um modo SHUTDOWN_CANCELLED é recebido, dois cenários principais determinam o comportamento do Orchestrator. Em ambos os casos, o modo de energia SHUTDOWN_CANCELLED é
adicionado ao final da fila. Em seguida, SHUTDOWN_CANCELLED é executado depois que
os elementos em fila são consumidos.
Cenário 1: o modo atual em andamento é um modo de energia
Se o Orchestrator estiver executando uma atualização do modo de energia, será solicitado o cancelamento do modo em andamento. Embora o Lifecycle Manager não ofereça suporte inerente ao cancelamento de uma transição em andamento, o Orchestrator verifica se nenhuma nova solicitação de pacote de serviços é iniciada.
Exemplo: se o instance-1 da configuração do exemplo anterior estiver em
processo de inicialização quando o modo SHUTDOWN_CANCELLED for recebido,
o instance-1 vai concluir a inicialização. No entanto, instance-2 e instance-3 não
prosseguem com as transições para o estado iniciado.
Cenário 2: um modo de energia existe na fila de processamento
Se o Orchestrator estiver processando uma atualização de modo que não seja de energia e houver uma solicitação de energia na fila de modos a serem executados, a transição de energia será removida da fila. Isso impede a execução.
Exemplo: usando a configuração do exemplo anterior, se o Orchestrator estiver
trabalhando em uma atualização não relacionada à energia (como uma atualização do veículo) e
SUSPEND_TO_RAM_ENTER estiver na fila, o recebimento de SHUTDOWN_CANCELLED resultará
em nenhuma das instâncias (instance-1, instance-2, instance-3) sendo
iniciadas.
Exemplo de implementação
O catálogo de um cliente que quer usar o código gerado pelo middleware para criar um cliente para o servidor RPC pode ser semelhante a este exemplo:
# proto-file: //system/software_defined_vehicle/vsidl/language/src/protos/sdv/vsidl/v1/syntax.proto
# proto-message: VsidlEntry
package: "package_name"
service_bundle {
name: "service_bundle_name"
client {
service: "com.android.sdv.orchestrator.CustomStateService"
}
}
Ao gerar código, é necessário adicionar a dependência ao catálogo do Orchestrator:
--dependency-catalog-path orchestration/engine/stable/vsidl/*
O código do cliente para enviar um novo valor é semelhante a este:
let fqin = ServiceFqin::builder()
.sdv_vm_name("vm_name")
.sdv_package_name("package_name")
.service_bundle_name("service_bundle_name")
.service_instance_name("instance_name")
.build()
.unwrap();
let context_ref = ContextRef::create(fqin);
let comms = Arc::new(SdvComms { context: context_ref });
// service_bundle_name is the bundle generated with middleware code that defines
// the RPC client to "com.android.sdv.orchestrator.CustomStateService".
let client = service_bundle_name::new(comms).await.unwrap();
let rpc_client = client
.create_rpc_client::<Client>(
UnitName::builder()
.vm_name(comms.context.get_self_fqin().get_sdv_vm_name())
.package_name("com.android.sdv.orchestrator")
.bundle_name("OrchestratorServiceBundle")
.service_unit_name(Client::DEFAULT_UNIT_NAME)
.build()
.unwrap(),
ClientOptions::default(),
)
.await;
let client = Arc::new(rpc_client.unwrap());
let request = SetCustomStateRequest {
mode: custom_mode_name,
value: custom_mode_value,
timestamp: MessageField::some(Timestamp::now()),
..Default::default()
};
let result = client.SetCustomState(&request).await;
// Process result
Ferramentas de depuração
O agente do Orchestrator é compatível com a ferramenta dumpsys. Para invocá-lo, execute o seguinte comando em uma instância de SDV em execução:
adb shell dumpsys com.google.sdv.ISdvAgent/orch
Use essa ferramenta para depurar e obter insights sobre o estado interno do agente do Orchestrator. Ao fazer isso, você verá:
- Estado atual dos modos: confira os modos de veículo, energia e personalizados ativos.
- Publicadores de modo personalizado: identifique quais serviços podem publicar modos personalizados (e em quais modos).
- Estado obrigatório por serviço: saiba o estado de cada pacote de serviços com base em condições predefinidas e modos atuais. Isso ajuda a diagnosticar por que um serviço pode não estar no estado esperado.
- Status de aplicação do modo: tenha uma visão clara de um modo restrito em andamento ou do último modo restrito, se nenhum estiver em andamento.
- Fila de aplicação de modo: confira os modos que estão aguardando para serem aplicados.
Exemplo:
AGENT NAME: SDV Agent dump - Orchestrator
AGENT FQIN: instance1:com.android.sdv.orchestrator.OrchestratorServiceBundle/default
AGENT STATE: See orchestrator state below.
----------------
----------------
INTERNAL STATE REPORTERS:
*NAME: Configuration state
*REPORT:
Active modes:
MODE VALUE TIMESTAMP (scs, ns)
Power POWER_OFF_EXIT -
Vehicle VEHICLE_ON -
Custom("CHARGING") ON 1750757590 (scs) 466507459 (ns)
Custom("TIRE_PRESSURE") front-left 1750757570 (scs) 554522995 (ns)
Modes allowed to publish by bundle (FQIN: modes):
com.android.sdv.sample.orchestration/CustomModeControlBundle: CHARGING, TIRE_PRESSURE
Requested state for instances:
STATE FQIN
Started com.android.sdv.sample.orchestration/CustomModeControlBundle/always-started-instance
Started com.android.sdv.sample.orchestration/OrchestratedServiceBundle/my-instance
Started com.sdv.oem.sample.diagnostics/DiagnosticsSampleWithDataItem/instance
Started com.sdv.oem.sample.diagnostics/DiagnosticsSampleWithEvent/instance
Started com.sdv.oem.user_preferences/UserPreferencesServiceBundle/default
----------------
*NAME: Engine state
*REPORT:
Last mode enforced was Custom("CHARGING") with value "ON"
Next modes to process: []
----------------
Para saber mais sobre os pacotes de serviços individuais gerenciados pelo Orchestrator, use o dumpsys do Lifecycle Manager da seguinte maneira:
dumpsys google.sdv.lifecycle.ILifecycleManager/default
Isso fornece informações detalhadas sobre o estado do ciclo de vida de cada serviço. Ao combinar a saída do dumpsys do Orchestrator com a saída do Lifecycle Manager, é possível ter uma visão completa do ciclo de vida dos pacotes de serviços na VM.