Implement business logic

  1. Verify that your VSIDL catalog directory has the necessary build, protobuf, and VSIDL files. To start from an existing sample, you can find an example of a valid catalog folder under /system/software_defined_vehicle/samples/vsidl/stable/catalog.

    Sample catalog structure:

    my_catalog/
    ├── Android.bp          # Defines rust_protobuf modules for .proto files
    ├── types.proto         # Protobuf message / RPC service definitions
    └── architecture.vsidl  # VSIDL service bundle definitions
    
  2. Write custom code to implement your business logic:

    • RPC servers: Implement the traits from lib.rs.
    • Publish and subscribe and RPC clients: Call the generated functions within service_bundle.rs to interact with other services.

    Rust

    1. Generate a skeleton implementation by running:

      vsidlc -c /path/to/catalog -o /path/to/output --services
      

      Running vsidlc with the --services flag generates a flag generates a boilerplate Rust implementation for each service bundle. This provides the necessary scaffolding—including the source code (main.rs) and a build configuration file (Android.bp) with all dependencies—allowing for immediate exchange of default messages while you focus on implementing the core business logic.

    2. For each service bundle, find the Rust implementation in:

      /path/to/output/services/ServiceBundleName/src/main.rs.

    3. 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, find the TODO comments in main.rs and adjust them to your preferences. For example:

      async fn handle_tire_pressure_range_unique_publisher(
          publisher: sdv::mw::Publisher<TirePressureRange>,
      ) {
          loop {
              // TODO: Modify the frequency of publishing messages here.
              sleep(Duration::from_secs(1)).await;
              // TODO: Modify the message content here.
              let message = TirePressureRange::default();
              info!("Publishing on TirePressureRange#UNIQUE");
              publisher.publish(&message).unwrap();
          }
      }
      
    4. To help ensure that your modifications to the generated files aren't overwritten the next time vsidlc runs, move the services folder out of the /path/to/output folder.

    C++

    1. Create a new header file, src/lib.hpp, with a class for your service bundle:

      #pragma once
      #include <sdv/service_bundle.h>
      #include <sdv/context.hpp>
      
      namespace com::sdv::oem::service_bundle {
      
      // Sample implementation of the service bundle interface.
      class ServiceBundleName : public android::sdv::service_bundle::ServiceBundle {
      public:
          ServiceBundleName(sdv_comms::ctx::Context context);
          ~ServiceBundleName();
          void onStart() override;
          void onStop() override;
      };
      }  // namespace com::sdv::oem::service_bundle
      
    2. Create a new source file, src/lib.cpp, with the class implementation:

      #include "src/lib.hpp"
      #include <sdv/sb_macro.h>
      #include <iostream>
      using com::sdv::oem::service_bundle::ServiceBundleName;
      
      // Register the new service bundle.
      REGISTER_SERVICE_BUNDLE(ServiceBundleName);
      
      // Sample implementation of the service bundle interface.
      namespace com::sdv::oem::service_bundle {
      // Creates a new instance of the ServiceBundleName.
      // Called when service bundle is created by the system.
      // Context object is provided as a parameter that gives access to the
      // communication stack APIs.
      ServiceBundleName::ServiceBundleName([[maybe_unused]] sdv_comms::ctx::Context context) :
        ServiceBundle(context) {
          // Memory allocations and static data loading should be done as
          // part of this method.
          //
          // Loading of the dynamic resources (sockets, files, etc)
          // is strongly discouraged due to possible Suspend-to-RAM scenario.
          //
          // The dynamic data can be loaded in the onStart method.
      }
      
      // Called when the service bundle is started by the system.
      void ServiceBundleName::onStart() {
          // Dynamic resources(sockets, files, etc) should be allocated during this call.
      }
      
      // Called when the service bundle is stopped by the system in preparation
      // for shutdown or suspend to RAM/Disc.
      void ServiceBundleName::onStop() {
          // Stop phase requires the service bundle to delete the dynamic resources
          // (sockets, files, etc) that were previously allocated in the onStart method.
      }
      
      // Called when the service bundle is destroyed by the system.
      ServiceBundleName::~ServiceBundleName() {
          // Static resources deallocation needs to be implemented in the destructor.
      }
      }  // namespace com::sdv::oem::service_bundle
      
    3. Create a service bundle library Soong target in a new or an existing Android.bp file:

      // Service bundle library.
      cc_library_shared {
        name: "libservice_bundle",
        srcs: ["src/lib.cpp",],
        // Allows the library to be available inside APEX.
        apex_available: [
            "//apex_available:platform",
            "//apex_available:anyapex",
        ],
        shared_libs: [
          // Service Bundle lifecycle C++ API.
          "libsdv_lifecycle_client_cpp",
          // commstack library that provides context object reference
          "libsdv_comms_cpp",
        ],
      
        // Service bundle package is packed into /product apexes.
        product_specific: true,
      }
      

What's next

To deploy your service bundles, see Build and deploy service bundles.