Configurar o orquestrador

O Orchestrator é um agente SDV local 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 realizadas em instâncias de pacote de serviços. Essas regras são baseadas nos modos 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.

Diagrama de configuração distribuída do orquestrador

Figura 1. Diagrama de configuração do orquestrador.

As configurações independentes do veículo não mudam dependendo do OEM ou do 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 variar em diferentes veículos de diferentes OEMs, embora a configuração possa ser igual para todas os veículos fabricados por um OEM específico.

APEXes de configuração

No tempo de execução, o Orchestrator vai ao registro de 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.

Por configuração de VM

Quando o orquestrador é iniciado, ele carrega e analisa a configuração da VM (se presente). 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:

  1. O sistema verifica a propriedade persist.sdv.orchestrator_config_path. Se ele tiver um valor, esse caminho será usado. Esse valor é mantido em reinicializações ou definido no ambiente de execução.

  2. Se persist.sdv.orchestrator_config_path estiver vazio, o sistema vai verificar a propriedade ro.boot.sdv.orchestrator_config_path. Se a propriedade ro.boot.sdv tiver um valor, esse caminho será copiado para a propriedade de persistência 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 receber o caminho do arquivo de configuração. Essa é uma propriedade persistente, ou seja, o valor dela é salvo nas reinicializações do dispositivo. É possível mudar o valor no tempo de 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 tempo de 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 tempo de execução

Definir a propriedade no tempo de execução é útil para testes ou para fazer mudanças temporárias, já que o valor é salvo nas reinicializações:

adb root
adb shell setprop persist.sdv.orchestrator_config_path {$path_to_file}.textproto

Definir a propriedade no tempo de 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 é definido, ele não é substituído por essa propriedade nas inicializações subsequentes.

É 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 tempo 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 InstanceToGroupMapping permite 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.

  • InstancesStates define os diferentes estados da instância do pacote de serviços.

  • InstancesStateConfiguration define o estado (de InstancesStates) em que uma instância de pacote de serviços precisa ser definida se a condição for avaliada como true.

  • ServiceBundleConfig contém informações sobre o pacote de serviços específico e as instâncias dele. Ele contém, para o pacote, os respectivos InstanceToGroupMapping e InstancesStateConfiguration.

  • CustomModes define uma lista de modos personalizados que o pacote pode publicar. O 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 necessária do pacote

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"
}

Esta declaração define instâncias de pacote de serviços n com os FQINs respectivos:

vm_name.package_name.service_bundle_name.instance_1

vm_name.package_name.service_bundle_name.instance_n

O nome da VM não foi 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

Declarar 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 orquestrador precisa saber em 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 deve ser aplicado se a condição for avaliada como true.

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 de um pacote de serviços, é possível adicionar instâncias a grupos. Em seguida, é possível configurar grupos no nível de configuração da 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

Confira um exemplo de esquema 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, oferecendo 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 ao estado especificado.

O orquestrador não garante a ordem em que a mudança de estado é executada. O orquestrador promove cada instância para o 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 orquestrador, 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 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

Confira um exemplo de esquema 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 definem quando uma instância ou um grupo de serviço é 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 definida seja aplicado. Uma condição tem estas características:

  • Expressão booleana arbitrariamente complexa (formada com expressões and ou not) 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, ou seja, é avaliado como true.

Estados de instâncias e grupos

instances_states e groups_states têm estas características.

  • Ditar quais estados são necessários para o agente de orquestração aplicar às instâncias ou grupos especificados, considerando que o estado é active

  • Ao aplicar um estado ao grupo, ele é aplicado a cada instância de pacote de serviços no grupo. Não há uma ordem específica para quando as instâncias são trazidas para o estado.

Os estados disponíveis incluem:

  • started depois que Service::on_start for chamado.

  • created

    • depois que Service::new é chamado, mas antes que Service::on_start seja chamado.

      OU

    • depois que Service::on_stop é chamado, mas antes que Service::drop seja chamado.

  • destroyed depois que Service::drop for chamado.

Conjunto de regras

Um estado de configuração pode ser ativo ou inativo, dependendo da condição. Vários estados podem estar ativos a qualquer momento. 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:

    1. destroyed tem precedência absoluta.
    2. started tem precedência antes de created.

Esquema proto

Confira um exemplo de esquema 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 e reinicialização de falhas

O orquestrador oferece um mecanismo robusto para lidar com falhas de pacotes de serviços e falhas de transição de ciclo de vida. Como o Orchestrator tem uma visão holística dos estados de 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 do pacote de serviços ao orquestrador por meio de notificações de morte do binder. Para evitar chamadas desnecessárias do binder para o LM, o Orchestrator armazena em cache o último estado de cada pacote (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 estiver definida, o valor será 0.

  • max_retries: define o número de vezes que o Orchestrator tenta novamente uma transição após uma falha temporária ou uma notificação de falha de pacote. O contador de repetição é redefinido para o valor de max_retries após uma operação bem-sucedida ou quando um novo modo é processado. Se vários mapeamentos segmentarem a mesma instância, o mapeamento com o max_retries mais 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 vários cenários de falha de maneira adequada:

  • 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 trazer o pacote de volta 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á enfileirada 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 está sendo reiniciado (ou está na fila de instâncias para reiniciar), ele 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 erros retornados pelo Lifecycle Manager para determinar a estratégia de repetição:

  • Erros transitórios (SERVICE_NOT_FOUND, OPERATION_FAILED, INTERNAL_ERROR): o Orchestrator tenta novamente a operação sem realizar nenhuma ação especial de limpeza.
  • Erros persistentes (VALUE_CORRUPTED, INVALID_ARGUMENT): o Orchestrator pressupõe que o pacote de serviços pode estar em um estado corrompido e tenta encerrar a instância do serviço antes de tentar novamente 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 trazer cada instância ao último estado solicitado.

Para evitar loops de recuperação infinitos para pacotes que falham ou falham repetidamente, o contador de novas tentativas só é redefinido para max_retries depois que uma operação de ciclo de vida é concluída 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 tentativas seja redefinido.

Relatório 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. Com essa interface, o orquestrador informa ativamente:

  • Estado do ciclo de vida:o estado pretendido da instância com base na configuração atual e nos modos ativos (como iniciada, criada ou destruída).
  • Estado de recuperação:o status de alcançar o estado de ciclo de vida pretendido, indicando se a instância está operacional, tentando novamente após uma falha ou não conseguiu se recuperar depois de esgotar todas as novas tentativas.

Essas informações são informadas para todas as instâncias, incluindo aquelas que não se registraram para monitoramento de pulsação. O HM usa essas informações para implementar as APIs e informar o estado da VM. Saiba mais em Monitoramento de integridade.

Exemplos

Nesta seção, apresentamos exemplos de configuração de estados com condições.

Exemplo de serviço básico
  • Não tem condição e, portanto, está sempre ativo.
  • 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 HVAC
  • Condição: ativo quando custom_state != occupancy.OCCUPANCY_EMPTY || custom_state == preheat.PREHEAT_ON.

  • Declara várias instâncias de serviço relacionadas a 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 a HVAC como destroyed.

  • Neste exemplo, quando os modos SYSTEM_POWER_LOW e RANGE_EXT_ON estão ativos, o app de HVAC é executado sem a instância de serviço RefrigerantLoop:

    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 ele está ligado.

  • Declara que os sensores de temperatura estão em execução.

  • Quando alguém está no carro, itens como 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 proto no nível do pacote de serviços que apresenta um pacote com duas instâncias, cada uma sendo parte de um grupo

  • Uma configuração 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 do gerenciamento de pacotes

A propriedade do sistema ro.boot.sdv.max_bundles_management_threads é um parâmetro de ajuste fundamental 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:

  1. Orchestration Engine:esse serviço lê a propriedade para determinar quantas chamadas simultâneas (por exemplo, startService, stopService) o Orchestrator pode fazer para o 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.

  2. 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 threads suficientes para processar as solicitações simultâneas do orquestrador.

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 esta linha no arquivo BoardConfig.mk apropriado e faça a reconstrução.

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 performance na 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. O objetivo principal é 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 VSIDL, monitor de integridade, descoberta de serviços, túnel de dados, RPC, VPM e telemetria). Ao iniciar o Orchestrator cedo 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 à disputa de recursos, deixando o processo de inicialização mais lento. Para minimizar esse problema, uma ordem de inicialização sequencial é aplicada usando propriedades do sistema:

  1. Registro de pacotes de serviços:começa primeiro para carregar todos os metadados de pacotes de serviços.
  2. Lifecycle Manager e Orchestrator: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 pacotes de serviços imediatamente.
  3. Outros agentes de SDV:só começam depois que o orquestrador está pronto.

Essa sequência controlada garante que o Orchestrator tenha prioridade para usar os recursos do sistema e iniciar os pacotes de serviços o mais rápido possível, resultando em 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 dos modos de veículo e de energia transmitidos pelo VPM. Após o estabelecimento da conexão inicial entre o Orchestrator e o sistema de gerenciamento de veículo e energia (VPM, na sigla em inglês), o Orchestrator define as seguintes propriedades booleanas do sistema como true:

  • sdv.orchestrator.bootup.power_mode.ready

  • sdv.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 devem estar em execução com base nos valores atuais dos modos recebidos. Em seguida, o orquestrador 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 de SDV sejam informados sobre o estado atual do veículo, como modo operacional (por exemplo, em estacionamento ou direção) e status de energia (por exemplo, ligado e suspenso). O orquestrador avalia esses valores para definir quais pacotes de serviços devem ser executados com base na configuração do orquestrador. Para saber mais, consulte Gerenciamento de veículos e energia.

Modos personalizados

Devido à quantidade de opções, não é possível modelar todos os modos de veículo. Cada OEM tem necessidades diferentes, e a padronização dos modos de veículo não atende a todos os casos de uso. Por isso, oferecemos suporte a modos específicos do 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 orquestrador.

  • 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 UNDEFINED quando nenhum valor é definido.

  • Função de orquestrador:o orquestrador atua como um receptor passivo de valores de modo personalizado.

  • Função do pacote de serviços:cada pacote de serviços pode ter vários modos personalizados e publicar novos valores em 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 diferentes fontes.

  • Responsabilidade de validação:os OEMs são responsáveis por garantir transições de estado válidas. O Orchestrator aceita qualquer novo valor.

Características estimadas:

  • Contagem estimada:os modos de energia e veículo gerenciam o ciclo de vida da maioria dos pacotes de serviços, e os modos personalizados têm uma função complementar. Esperamos que a ordem de magnitude dos modos personalizados seja de dezenas, não centenas.

  • Tempo estimado:os modos não são enviados periodicamente. Em vez disso, os modos são acionados por eventos, como abrir uma porta, iniciar uma sequência de estacionamento, iniciar um ciclo de carregamento e outros eventos semelhantes de importância definida pelo OEM.

O design do modo personalizado oferece flexibilidade para definir e gerenciar modos específicos, permitindo que o orquestrador permaneça independente da lógica da máquina de estado subjacente.

Modos compatíveis

Para garantir que os pacotes de serviços sejam publicados apenas nos modos personalizados que possuem, cada um precisa declarar explicitamente a lista de modos personalizados próprios 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, possui), 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 do proto do Orchestrator (o componente Condition) oferece suporte à 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 orquestrador 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 o valor deve ser propagado para outras VMs. Depois da propagação, cada orquestrador revisa a configuração para encontrar a lista de pacotes de serviços cujo 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 enfileirados são consumidos.

Cenário 1: o modo em andamento atual é um modo de energia

Se o Orchestrator estiver executando uma atualização do modo de energia, será solicitada uma interrupção 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 foi iniciada.

Exemplo: se instance-1 da configuração no exemplo anterior estiver em processo de inicialização quando o modo SHUTDOWN_CANCELLED for recebido, 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 está na fila de processamento

Se o Orchestrator estiver processando uma atualização de modo não energético 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 dele.

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, receber SHUTDOWN_CANCELLED resultará em nenhuma das instâncias (instance-1, instance-2, instance-3) sendo iniciada.

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 parecido com 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, adicione 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 é assim:

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 tem suporte para a ferramenta dumpsys. Para invocar, execute o comando a seguir em uma instância do SDV em execução:

adb shell dumpsys com.google.sdv.ISdvAgent/orch

Use essa ferramenta para depurar e entender o estado interno do agente do Orchestrator. Isso vai mostrar:

  • Estado dos modos atuais:confira os modos ativo, de energia e personalizados do veículo.
  • Editores do modo personalizado:identifique quais serviços podem publicar modos personalizados (e em quais modos).
  • Estado necessá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 da 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 aguardando aplicação.

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 pacotes de serviços individuais gerenciados pelo Orchestrator, use o dumpsys atual 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. A combinação da saída dumpsys do Orchestrator com a saída do Lifecycle Manager apresenta uma imagem completa do ciclo de vida dos pacotes de serviços na VM.