Quick start: Create and execute SDV service bundles

This page shows you how to create an SDV service bundle, pack it into the APEX file format, and execute it on a virtual Cuttlefish device. The primary units of code execution on the SDV system are the service bundles. Service bundles provide lifecycle methods that the system executes when the system state changes.

  1. Set up your build environment

    From within your working directory, source the envsetup.sh script to set up your build environment, set Cuttlefish as build target, and build the vsidlc code generator:

    source build/envsetup.sh
    lunch sdv_core_cf-trunk_staging-userdebug
    m vsidlc
    
  2. Define your architecture by creating a folder named my_catalog with these files:

    • tire.proto: Defines the data structure you are sending:

      syntax = "proto3";
      
      package com.android.sdv.sample.quickstart;
      
      import "google/protobuf/empty.proto";
      import "sdv/vsidl/v1/annotations.proto";
      
      message TirePressure {
        uint32 pressure = 1;
      }
      
      message TireInfoResponse {
        string serial_number = 1;
        uint32 max_pressure = 2;
      }
      
      service TireService {
        rpc GetTireInfo(google.protobuf.Empty) returns (TireInfoResponse);
      }
      
    • architecture.vsidl: Defines the service bundles and their communication:

      package: "com.android.sdv.sample.quickstart"
      
      service_bundle {
        name: "Manager"
      
        publisher {
          message: "TirePressure"
          topic: "pressure"
          capacity: 8
        }
      
        server {
          service: "TireService"
          channel: "tire-service"
        }
      }
      
      service_bundle {
        name: "Monitor"
      
        subscriber {
          message: "TirePressure"
          topic: "pressure"
        }
      
        client {
          service: "TireService"
          channel: "tire-service"
        }
      }
      
    • Android.bp: Defines the build process:

      rust_protobuf {
          name: "libquickstart_sample_tire_proto",
          crate_name: "quickstart_sample_tire_proto",
          protos: [
              "tire.proto",
          ],
          source_stem: "quickstart_sample_tire_proto_source",
          rustlibs: [
              "libvsidl_v1_stdlib_proto_rs",
          ],
          proto_flags: [
              "-I external/protobuf/src",
          ],
          vendor_available: true,
          product_available: true,
          apex_available: [
              "//apex_available:platform",
              "//apex_available:anyapex",
          ],
          min_sdk_version: "35",
      }
      
      filegroup {
          name: "vsidl_quickstart_catalog",
          srcs: ["**/*"],
      }
      
  3. Generate a skeleton implementation and config files by running:

    vsidlc -c /path/to/catalog -o /path/to/output --services --apex
    
  4. For each service bundle, find the Rust implementation in /path/to/output/services/ServiceBundleName/src/main.rs.

  5. By default, the generated implementation creates messages with default values and sends them between publishers and subscribers or RPC clients and servers. To modify this behavior, look for the TODO comments in main.rs and adjust them to your preferences. For example:

      async fn handle_tire_pressure_front_left_publisher(publisher: sdv::mw::Publisher<TirePressure>) {
          loop {
              // TODO: Modify the frequency of publishing messages here.
              sleep(Duration::from_secs(1)).await;
              // TODO: Modify the message content here.
              let message = TirePressure::default();
              info!("Publishing on TirePressure#FRONT_LEFT");
              publisher.publish(&message).unwrap();
          }
      }
    
  6. To include your bundle in the system image, add the generated APEX module name to your product makefile.

    1. Open your product makefile (for example, /device/google/sdv/sdv_core_base/sdv_samples_core_services.mk).

    2. Add the module to PRODUCT_PACKAGES by adding:

      PRODUCT_PACKAGES += com.android.sdv.sample.quickstart
      PRODUCT_PACKAGES += com_android_sdv_sample_quickstart_orchestration_configurations
      
  7. Build the image:

    m
    
  8. Run and verify the service bundle execution:

    1. Start a virtual Cuttlefish device:

      sdv-cf create --instance_name=instance1
      
    2. Run the service bundle when the system is booted:

      Manually

      adb wait-for-device
      adb root
      adb shell sdv_service_bundle start local-vm:com.android.sdv.sample.quickstart.Manager/instance
      adb shell sdv_service_bundle start local-vm:com.android.sdv.sample.quickstart.Monitor/instance
      

      Orchestrator

      adb wait-for-device
      adb root
      # Apply global orchestration configuration by setting system property
      adb shell setprop persist.sdv.orchestrator_config_path "etc/orch/vm_quickstart_orch_config.textproto"
      adb reboot
      
    3. Verify the execution of the service bundle:

      adb logcat *:F com_android_sdv_sample_quickstart_Manager_instance:* com_android_sdv_sample_quickstart_Monitor_instance:*
      

      This command displays logs that show the service bundles exchanging messages:

      03-30 13:41:31.505   967   976 I com_android_sdv_sample_quickstart_Manager_instance: sdv_lm_manager: Publishing on TirePressure#PRESSURE
      03-30 13:41:31.505   983   991 I com_android_sdv_sample_quickstart_Monitor_instance: sdv_lm_monitor: Received message on TirePressure#PRESSURE: [TirePressure { pressure: 0, special_fields: SpecialFields { unknown_fields: UnknownFields { fields: None }, cached_size: CachedSize { size: 0 } } }]
      03-30 13:41:31.626   983   991 I com_android_sdv_sample_quickstart_Monitor_instance: sdv_lm_monitor: Sending request on Monitor/TireService
      03-30 13:41:31.627   967   976 I com_android_sdv_sample_quickstart_Manager_instance: sdv_lm_manager: Received request on Manager/TireService: Empty { special_fields: SpecialFields { unknown_fields: UnknownFields { fields: None }, cached_size: CachedSize { size: 0 } } }
      03-30 13:41:31.627   983   991 I com_android_sdv_sample_quickstart_Monitor_instance: sdv_lm_monitor: Received response on Monitor/TireService: TireInfoResponse { serial_number: "", max_pressure: 0, special_fields: SpecialFields { unknown_fields: UnknownFields { fields: None }, cached_size: CachedSize { size: 0 } } }
      

What's next

Learn more about the Vehicle Services Interface Definition Language (VSIDL) and how to define your own service bundles from scratch.