语言规范

此页面使用 ISO/IEC 14977 扩展的 Backus-Naur 范式 (EBNF) 和 protobuf 指定了车辆服务接口定义语言 (VSIDL)。本页重点介绍该语言的无上下文语法和语言元素的含义。

语言层次结构

根据 Meta Object Facility (MOF),VSIDL 编译器 (VSIDLC) 使用图 1 中所示的语言:

VSIDLC 语言

图 1. VSIDLC 语言。

VSIDLC 主要基于协议缓冲区 (protobuf) 语言。Protobuf 用于指定通过发布-订阅和远程过程调用 (RPC) 调用的方式交换的数据类型。从技术角度来看,VSIDL 模型是 TextProto 文件,其中 VSIDL 的语法在 protobuf 文件 (syntax.proto) 中定义。用于指定数据类型的 protobuf 文件和 VSIDL 模型都用于生成 Rust 代码。生成的代码主要包含实现交换消息的数据结构的结构体,以及实现用于在 Rust 中创建服务单元的函数的 Rust 函数,而不会自动调用这些服务单元。生成的 Rust 代码附带自定义 Rust 代码,后者使用前者来实例化服务单元并实现应用的业务逻辑。

抽象语法

图 2 显示了 VSIDL 中的核心消息类型:

VSIDL 中的核心消息类型

图 2. VSIDL 中的核心消息类型。

VSIDL 条目

本部分介绍了 VSIDL 条目消息类型。

EBNF 语法

start VsidlEntry =
  "package" , ":" , String , ";" ,
  { "service_bundle" , ServiceBundle } ,
  { "extension" , ":" , Any } ,
  { "some_ip_mapping" , ":" , SomeIpMapping } ,
  { "vhal_mapping" , ":" , VhalMapping }
;

Proto 定义

// The root message for VSIDL files
message VsidlEntry {
  // Required. Package name for entities mentioned in the file.
  string package = 1;
  // Enables custom extensions beyond the standard VSIDL model.
  repeated google.protobuf.Any extension = 3;

  // SOMEIP mapping rules
  repeated sdv.someip.v1.SomeIpMapping some_ip_mapping = 4;
  // VHAL mapping rules
  repeated VhalMapping vhal_mapping = 5;
  // List of SDV service bundles defined in the file.
  repeated ServiceBundle service_bundle = 6;
}

用法示例

package: "com.android.sdv.sample.vsidl"

service_bundle {
  name: "Manager"

  publisher {
    message: "TirePressure"
    topic: "front-left"
    topic: "front-right"
    capacity: 10
  }
}

说明

VsidlEntry 消息用作 VSIDL 文件(扩展名为 .vsidl)的根容器。此消息封装了单个 VSIDL 文件中的所有定义和配置。VsidlEntry 是绑定所有其他元素的顶级元素。

用途

  • 定义 VSIDL 文件的总体结构。
  • 指定文件中所有实体的软件包命名空间。
  • 包含一组服务包定义。
  • 允许对 VSIDL 模型进行自定义扩展。
  • 包含 SOME/IP 和 VHAL 的映射规则。

限制条件

  • 软件包名称 (E211):软件包名称不得超过 127 个字符。
  • 悬空文件 (E10B):目录中的所有文件都必须在 Android.bp 文件组中引用。

服务包

本部分介绍了服务包消息类型。

EBNF 语法

ServiceBundle = "{" , { ServiceBundleElement } , "}" ;

ServiceBundleElement =
  "name" , ":" , String |
  "publisher" , Publisher |
  "subscriber" , Subscriber |
  "server" , Server |
  "client" , Client |
  "extension" , ":" , Any |
  "diagnostics_declaration" , DiagnosticsDeclaration |
  "build_cfg" , BuildConfiguration |
  "register_reflection_metadata" , Boolean
;

Proto 定义

// Defines an SDV service
message ServiceBundle {
  // Required. Name of the service bundle (without the package name).
  string name = 1;
  // List of publications the service bundle provides.
  repeated Publisher publisher = 2;
  // List of publications a service bundle subscribes to.
  repeated Subscriber subscriber = 3;
  // RPC services offered by a service bundle.
  repeated Server server = 4;
  // RPC services consumed by a service bundle.
  repeated Client client = 5;
  // Enables custom extensions beyond the standard VSIDL model.
  repeated google.protobuf.Any extension = 7;

  // Diagnostics declarations
  sdv.diagnostics.v1.DiagnosticsDeclaration diagnostics_declaration = 8;

  // Build Configuration
  optional BuildConfiguration build_cfg = 9;

  // Register metadata for service units provided by this service bundle.
  // Setting this to true will increase the memory footprint
  // and network load significantly.
  bool register_reflection_metadata = 10;
}

用法示例

service_bundle {
    name: "SeatController"

    publisher {
      message: "SeatHeating"
      topic: "driver-seat"
      capacity: 10
    }
}

说明

服务包定义了相关服务、发布者、订阅者、RPC 服务器和 RPC 客户端的逻辑分组。服务包充当特定功能集及其互动的容器。

限制条件

  • 软件包名称要求(E209、E20A、E20B、E20C)
    • 服务包必须具有已填充的名称。
    • 名称必须以有效的 Unicode 标识符起始字符(通常是字母)开头。
    • 名称中的后续字符必须是有效的 Unicode 标识符继续字符(通常是字母或数字)。
    • 该名称不得是 Rust、Java 或 C++ 中的预留关键字。
  • 全局软件包唯一性 (E309):每个服务软件包都必须具有唯一的完全限定名称(基于其软件包和名称)。
  • 内部唯一性(E100、E300、E302、E303、E308)
    • 在单个服务软件包中,每个 RPC 服务最多只能由一个服务器定义提供。
    • 在单个服务包中,每种MULTI_PUB发布内容类型最多只能由一个发布者定义发布。
    • 在单个服务包中,所有用户定义的服务单元名称(对于发布者或服务器)都必须是唯一的。
    • 在单个服务包中,所有服务单元名称(无论是用户定义的还是自动生成的)都必须具有唯一性。
  • build 目标命名惯例(E205、E206、E207、E208):如果提供了自定义 build 目标名称 (build_cfg.target_name),则该名称必须遵循蛇形命名法格式(小写字母、数字和单个下划线,不能以单个下划线开头或结尾)。
  • build 目标名称唯一性 (E301):用户定义的 build 目标名称不得与任何其他服务包的自动生成目标名称冲突。

发布商

本部分介绍了发布者消息类型。

EBNF 语法

Publisher = "{" , { PublisherElement } , "}" ;

PublisherElement =
  "message" , ":" , String |
  "topic" , ":" , String |
  "capacity" , ":" , Integer |
  "service_unit_name" , ":" , String
;

Proto 定义

// Represents a publisher within a service bundle.
message Publisher {
  // Name of the service unit. Name may only use characters from [a-z0-9\-]+,
  // must start with [a-z], may not end with a hyphen,
  // and may not contain consecutive hyphens.
  string service_unit_name = 3;
  // Required. The type of data being published.
  string message = 4;
  // Required. The number of messages a publication queue can hold.
  // Must be an even number >= 2.
  int64 capacity = 6;
  // Required. Unique identifier for the publication topic.
  // Must be in lowercase dash-case.
  repeated string topic = 7;
}

用法示例

publisher {
  message: "SeatHeating"
  topic: "driver-heating"
  capacity: 10
}

说明

Publisher 消息类型定义了 ServiceBundle 提供的数据源。此消息类型用于指定所发布的数据类型以及该数据的特定主题和容量。

主题

每个 Publisher 实例都有一个 message 字段,用于引用正在发布的 proto 消息。它必须指定主题(以 topic 表示)和容量(以 capacity 表示)。

  • 主题:发布主题的唯一标识符。必须采用小写连字号格式(例如 my-topic)。
  • 容量:指定队列的大小,即队列在丢弃未读消息之前可以容纳的消息数量。必须是大于或等于 2 的偶数。

用户定义的名称

发布商可以拥有用户定义的服务单元名称,以替换自动选择的服务单元名称。此可供性可用于选择较短的名称。如果发布商使用用户定义的服务单元名称,则可能仅使用一个实例,以便将服务单元名称唯一分配给一个实例。

# VALID: A publisher assigns a user-defined name to a single instance
publisher {
  message: "SeatHeating"
  topic: "seat-heating-status"
  service_unit_name: "heating-is-off"
}

# ERROR: user-defined names are only allowed if there's only a single instance
publisher {
  message: "SeatHeating"
  topic: "seat-heating-status"
  service_unit_name: "heating-status"
}

限制条件

  • 发布商展示位置 (E300):同一 MULTI_PUB 类型的发布商必须在单独的服务包中定义。
  • 本地名称唯一性 (E302):在任何单个服务包中,所有发布商都必须具有唯一的用户定义的服务单元名称。
  • 全局名称唯一性 (E304):同一出版类型的发布商必须在所有服务包中具有全局唯一的用户定义的服务单元名称。
  • 为单个渠道命名 (E306):用户定义的服务单元名称只能分配给处理一个实例的发布商。
  • 单个发布者限制 (E307):标记为 SINGLE_PUB 的 protobuf 消息只能由整个系统中的一个发布者发布。
  • 服务单元名称的唯一性 (E308):所有服务单元名称(无论是生成的还是用户定义的)在其服务包内都必须是唯一的;用户定义的名称应用于解决与生成的名称之间的冲突。
  • 变体规范要求 (E501):如果发布者为具有多个变体的类型使用用户定义的名称,则必须明确指定其发布的变体。
  • 订阅者的发布商存在性 (E504):每个已定义的订阅者都需要至少一个相应发布商(针对指定类型和变体)。
  • 有效的发布者类型 (E601):发布者必须引用与现有 protobuf 消息对应的类型。
  • 发布注释要求 (E602):发布者引用的 protobuf 消息类型必须包含 SdvPublication 注释。
  • 有效的变体使用情况 (E606):如果发布商指定了变体(实例),则该变体必须存在于 protobuf 中为发布类型定义的 instances_enum 内。
  • 变体规范条件 (E607):仅当发布类型在 protobuf 中定义了 instances_enum 时,发布商才能指定明确的变体(实例)。
  • 主题命名 (E20D、E20F):主题必须采用小写短划线格式,且不得超过 127 个字符。
  • 主题唯一性 (E314):主题必须在所有发布者之间具有全局唯一性。
  • 容量要求(E406、E407):容量是必需的,且必须是大于或等于 2 的偶数。

订阅者

本部分介绍了订阅者消息类型。

EBNF 语法

Subscriber = "{" , { SubscriberElement } , "}" ;

SubscriberElement =
  "message" , ":" , String |
  "topic" , ":" , String
;

Proto 定义

// Represents a subscriber within a service bundle.
message Subscriber {
  // Required. The type of data being subscribed to.
  string message = 4;
  // Required. Specific topic(s) of the message to subscribe to.
  // Must match the publisher's topic.
  repeated string topic = 6;
}

用法示例

subscriber {
  message: "SeatHeating"
  topic: "driver-seat"
}

说明

Subscriber 消息定义了 ServiceBundle 提供的发布接收器。此消息指定了订阅的数据类型以及相应发布的具体主题。如果某个主题有多个发布者,订阅者会收到所有发布者发布的消息。

限制条件

  • 发布者存在性 (E504):对于每个已定义的订阅者,必须至少存在一个发布相应发布类型和变体的发布者。
  • 有效的订阅类型 (E608):订阅者必须引用与使用 SdvPublication 注释定义的现有 protobuf 消息对应的类型。
  • 有效的变体订阅 (E609):如果订阅者指定了变体(实例),则该变体必须是相应 protobuf 发布类型 instances_enum 中定义的有效值。
  • 主题为必需项 (E408):订阅者必须提供主题。
  • 主题重新声明 (E311):订阅者主题不应在同一服务包中重新声明。

RPC 服务器

本部分介绍了 RPC 服务器消息类型。

EBNF 语法

Server = "{" , { ServerElement } , "}" ;

ServerElement =
  "service" , ":" , String |
  "channel" , ":" , String |
  "service_unit_name" , ":" , String
;

Proto 定义

// Represents an RPC server within a service bundle.
message Server {
  // Deprecated. Name of the service unit.
  string service_unit_name = 3 [ deprecated = true ];
  // Required. Name of the RPC service.
  string service = 4;
  // Required. Name of the RPC channel.
  // Must be in lowercase dash-case.
  string channel = 5;
}

用法示例

server {
  service: "SetTemperature"
  channel: "temp-setter"
}

说明

Server 消息定义了 ServiceBundle 提供的 RPC 服务器。此消息指定服务器实现的服务和 RPC 渠道。

RPC 服务器会公开一组方法,供客户端远程调用。service 字段用于指定服务器实现的 RPC 服务的名称。此服务在 proto 文件中定义,并在自定义 Rust 代码中实现。RPC 服务可以包含一元方法、客户端流式方法和服务器流式方法,如 protobuf 服务定义中所述。channel 字段定义了通信端点,是必需字段 (E409)。

限制条件

  • 服务定义 (E603):RPC 服务器必须指定与现有 protobuf RPC service 值对应的 service 值。
  • 每个服务的服务器数量限制 (E100):在单个服务软件包中,特定 RPC service 最多只能由一个服务器定义提供。
  • 渠道命名 (E20E):RPC 渠道必须采用小写短划线命名法。
  • 渠道为必需项 (E409):RPC 渠道为必需项。
  • 渠道唯一性 (E40B):RPC 渠道只能由一项服务使用。

RPC 客户端

本部分介绍了 RPC 客户端消息类型。

EBNF 语法

Client = "{" , { ClientElement } , "}" ;

ClientElement =
  "service" , ":" , String |
  "channel" , ":" , String
;

Proto 定义

// Represents an RPC client within a service bundle.
message Client {
  // Required. Name of the RPC service.
  string service = 2;
  // Required. Name of the RPC channel.
  // Must match the server's channel and be in lowercase dash-case.
  string channel = 3;
}

用法示例

client {
  service: "SetTemperature"
  channel: "temp-setter"
}

说明

client 定义了 ServiceBundle 使用的 RPC 客户端。client 指定客户端与之互动的服务以及要连接的渠道。客户端可以与一元方法、客户端流式方法和服务器流式方法进行交互,具体取决于服务定义。

限制条件

  • 服务定义 (E60A):RPC 客户端必须指定与现有 protobuf service 定义对应的 service
  • 唯一服务源 (E60B):RPC 客户端的 service 引用的 protobuf service 定义必须在所有 protobuf 文件中唯一定义(不得多次定义)。
  • 渠道为必需项 (E409):RPC 渠道为必需项。

构建配置

本部分介绍了 Build 配置消息类型。

EBNF 语法

BuildConfiguration = "{" , BuildConfigurationElement, "}" ;

BuildConfigurationElement =
  "target_name" , ":" , String |
  "skip_codegen" , ":" , Boolean
;

Proto 定义

// Defines additional information used to configure build settings
message BuildConfiguration {
  /// Build target name
  optional string target_name = 1;
  // Do not generate code for this service bundle
  optional bool skip_codegen = 2;
}

用法示例

build_cfg {
  target_name: "my_custom_target_name"
  skip_codegen: false
}

说明

BuildConfiguration 用于配置 ServiceBundle 的非标准形参,以进行代码生成。所有 build 配置都是可选的。

  • target_name(可选,string):指定 Android.bp 文件中 build 目标的名称。使用此属性可设置比自动选择的名称更短的目标名称。
  • skip_codegen(可选,bool):表示是否应跳过相应服务包的代码生成。如果设置为 true,则不会为此特定服务包生成任何代码。这对于手动实现的服务包非常有用。默认情况下,该设置为 false

限制条件

  • 目标名称格式(E205、E206、E207、E208):如果提供了自定义 build 目标名称 (build_cfg.target_name),则必须严格遵循 snake_case 格式:
    • 只能包含小写字母(az)、数字(09)和下划线 (_)。
    • 不得包含连续的下划线 (__)。
    • 不得以下划线开头。
    • 不得以下划线结尾。
  • 目标名称唯一性 (E301):用户定义的 build_cfg.target_name 在整个构建系统中必须是唯一的,并且不得与从其他服务软件包定义派生的任何自动生成的目标名称冲突。