All posts
Engineering · · 9 min read

AAOS-SDV goes open source — what it means for ECU diagnostics engineers

Google open-sourced AAOS-SDV this month. Most coverage has focused on what Android Automotive can do on the infotainment side. That's not where the interesting engineering challenge is.

The interesting challenge is in the ECUs your AAOS-SDV platform is supposed to manage.

AAOS-SDV is a headless Android OS designed to run not just in the IVI domain, but across multiple vehicle compute domains — body controllers, instrument clusters, ADAS coprocessors. It runs in a multi-VM environment using VirtIO drivers on a single SoC. The infotainment VM gets the screen. The other VMs get the ECUs.

And those ECUs speak UDS.


What AAOS-SDV actually is

AAOS-SDV is not a version of Android you run on a head unit. It's a platform for consolidating multiple vehicle ECU functions onto a single automotive SoC in separate virtual machines — a body control module function, a cluster renderer, an ADAS preprocessing pipeline, all running in isolated VMs on the same piece of silicon.

The platform includes:

SDV Core OS: service discovery, inter-VM communication, system updates, power management.
SOME/IP broker: bridges SDV services to external ECU services on the legacy vehicle network.
Display Safety: functionally safe early-boot rendering for cluster warning indicators.
SDV Telemetry: on-device data collection for vehicle-to-cloud services.

The SOME/IP broker is the piece that connects AAOS-SDV to the rest of the vehicle. Classic ECUs — brake controllers, battery management systems, HVAC units — are still out there on the CAN bus, speaking their existing protocols. AAOS-SDV needs to reach them.


The SOME/IP bridge into the legacy network

SOME/IP (Scalable service-Oriented MiddlewarE over IP) is how AUTOSAR Adaptive ECUs expose their services. The AAOS-SDV SOME/IP broker discovers and consumes those services.

An ECU that wants to be first-class in an AAOS-SDV architecture exposes a SOME/IP interface with three things: Service Discovery (announce availability so the broker can find it), Methods (callable request/response operations), and Events and fields (push notifications for state changes — battery voltage below threshold, DTC set).

On the legacy CAN side, a gateway ECU translates between SOME/IP services and the underlying CAN frames. But for ECUs on the vehicle Ethernet backbone — which is increasingly the case in new architectures — the SOME/IP interface lives directly in the ECU firmware.

The UDS diagnostic layer runs underneath all of this. SOME/IP carries operational data and commands. UDS carries the diagnostic and programming functions: session control, security access, DID reads and writes, DTC management, software downloads. These are different concerns at different layers, and mixing them is an architecture mistake you pay for during OTA campaigns.


The diagnostic access problem

Android is HTTP-native. UDS is a binary protocol — request/response frames over ISO-TP, with a 7-byte header structure that has nothing in common with HTTP.

In a classic workshop environment, a diagnostic tool speaks UDS directly via a CAN adapter or DoIP socket. That works fine when a human is sitting in front of a laptop. It doesn't map cleanly to a service-oriented vehicle platform where diagnostics need to be initiated programmatically from an Android application, a vehicle cloud backend, or an automated OTA update sequence.

SOVD (Service Oriented Vehicle Diagnostics) is the standard that solves this. It exposes UDS operations as a RESTful API over vehicle Ethernet. An AAOS-SDV application that wants to read a DTC list sends an HTTP GET. An OTA routine that wants to trigger a software download sends an HTTP POST to start the programming sequence. The SOVD layer translates these into the UDS binary frames and handles the session and transport state underneath.

The architecture in a consolidated SDV domain:

AAOS-SDV application
        │
        │  HTTP (SOVD REST)
        ▼
SOVD Central Diagnostic Application (CDA)
        │
        │  ISO 14229 UDS frames
        ├─── DoIP → ECU-A (Ethernet backbone)
        └─── SOME/IP gateway → CAN ECU-B, CAN ECU-C

Your ECU firmware sits at the bottom of this stack. It needs to speak UDS correctly. The SOVD CDA handles the translation upward.


What your ECU firmware actually needs

Three things, in order of criticality.

A correct UDS implementation. Not "a UDS implementation that works in the workshop." One that handles all session transitions cleanly, enforces security access before write operations, returns the right NRC codes, and doesn't leave orphaned sessions open after a reset. These bugs are rarely visible in manual testing. They show up when an OTA orchestrator runs a 40-step automated programming sequence at 3am and step 22 fails because the ECU is in the wrong session state.

DoIP transport. If your ECU is on a vehicle Ethernet backbone, it needs ISO 13400 DoIP for diagnostic access. The UDS frames are identical — the transport changes. DoIP adds an 8-byte gateway header, vehicle announcement/identification, and TCP-based connection management. EDS handles this with eds_doip_transport_t — the UDS core is unmodified, you swap the transport layer.

A clean service interface for SOME/IP operational data. Diagnostic data (DTCs, programming status, ECU reset) stays in UDS. Operational data that AAOS-SDV services need to consume continuously — vehicle speed, battery state of charge, door status — should be SOME/IP events or fields. Mixing these into UDS DID reads works but it's the wrong architecture: DID reads are request-response with overhead, SOME/IP events are push-based and efficient.


The SOVD CDA in EDS

When you run codegen with --sovd, it generates sovd_cda.json alongside the C firmware files. This is an OpenSOVD 1.0 Capability Description and Advertisement — a machine-readable JSON file that tells any SOVD-compatible client what your ECU can do: its DIDs, DTCs, routines, supported sessions, and transport parameters.

On a DoIP-enabled ECU, set the transport in diagnostics_config.yaml:

ecu:
  transport: doip
  doip:
    logical_address: "0xE400"
    source_address:  "0x0E00"
    port:            13400

Add --sovd to the codegen invocation:

python3 tools/codegen.py \
    --config diagnostics_config.yaml \
    --out    generated/ \
    --safety-wrappers --asil-level B --sovd

The output lands in generated/sovd_cda.json alongside the C files:

{
  "sovdVersion": "1.0.0",
  "ecuIdentification": {
    "name": "BMS_ECU",
    "version": "1.0.0",
    "logicalAddress": "0xE400",
    "sourceAddress": "0x0E00"
  },
  "transportInfo": { "protocol": "DoIP", "port": 13400 },
  "dataIdentifiers": [
    {
      "id": "0xD001",
      "name": "BatteryVoltage",
      "dataLengthBytes": 4,
      "access": ["read"],
      "minSession": "extended",
      "readSecurityLevel": 0
    }
  ],
  "dtcs": [
    {
      "code": "0xD00101",
      "description": "CellOvervoltage",
      "severity": "immediate_display"
    }
  ],
  "diagnosticServices": [ ... ]
}

An AAOS-SDV diagnostic orchestrator — or any SOVD-compatible client, such as Eclipse SDV tooling or an OEM gateway — reads this CDA to discover what the ECU exposes without needing out-of-band documentation. The CDA is generated at build time from the same diagnostics_config.yaml that drives the C firmware, so it stays in sync automatically.

When a live SOVD server is on the network (an OEM gateway or Eclipse SDV component exposing the SOVD REST interface), TestLab's SovdTester transport can drive it directly. Add the ECU to a workspace config:

ecus:
  - name: bms
    interface: sovd
    sovd_url: "http://169.254.100.10:20002"
    sovd_entity: "bms_ecu"

Then sovd_read_data, sovd_read_faults, sovd_clear_faults, and sovd_call_function campaign actions run over the SOVD REST interface — same YAML structure as any other TestLab campaign. The ECU firmware side is unchanged: it speaks UDS over DoIP whether the request arrived from a workshop tester, a TestLab campaign, or an AAOS-SDV orchestrator through a SOVD gateway.


Validate the stack with TestLab

OTA programming sequences are the most consequential diagnostic operations. A failed OTA sequence leaves an ECU in an undefined state — possibly unrecoverable without physical access. Programming sequences in UDS are long: switch to extended session, authenticate, switch to programming session, erase memory, transfer data in blocks, validate checksum, exit programming. Any step that fails or returns the wrong NRC triggers an error path.

TestLab lets you define and run this sequence against a virtual ECU before you have hardware:

# campaigns/ota_programming.yaml
campaign: ota_programming
steps:
  - action: session
    target: extended
    expect: positive

  - action: security_access
    level: 1
    expect: positive

  - action: session
    target: programming
    expect: positive

  - action: routine_control
    routine_id: "0xFF00"
    sub_function: start
    expect: positive

  - action: request_download
    memory_address: "0x08008000"
    memory_size: "0x40000"
    compression: 0x00
    encrypting: 0x00
    expect: positive

  - action: transfer_data
    block_sequence_counter: 0x01
    data_file: "firmware/bms_v2.bin"
    expect: positive

  - action: request_transfer_exit
    expect: positive

  - action: ecu_reset
    reset_type: hard
    expect: positive

Run it against the virtual ECU:

testlab-run \
    --config   diagnostics_config.yaml \
    --campaign campaigns/ota_programming.yaml \
    --virtual \
    --json     reports/ota_$(date +%Y%m%d).json

If the session gate fails — if your ECU accepts a RequestDownload without enforcing SecurityAccess first — the campaign catches it. If TransferData returns an unexpected NRC, you see it in the report before the ECU is anywhere near a vehicle.

When the virtual ECU passes, run the same campaign against hardware via DoIP:

testlab-run \
    --config   diagnostics_config.yaml \
    --campaign campaigns/ota_programming.yaml \
    --transport doip \
    --target    169.254.100.10 \
    --json      reports/ota_hw_$(date +%Y%m%d).json

Same YAML. Same assertions. The only change is the transport flag.


What AAOS-SDV doesn't change for you

The virtualization layer — VirtIO drivers, multi-VM setup, Cuttlefish cloud testing, Display Safety rendering — is the SoC integrator's problem, not the ECU firmware engineer's problem. If you're building ECU firmware for an AAOS-SDV-managed vehicle, none of that affects your code.

The build requirements to work with AAOS-SDV source directly are heavy (400 GB disk, 64 GB RAM, 6-hour builds). You don't need any of that. Your interface to AAOS-SDV is UDS over DoIP or SOME/IP — standard protocols with existing tooling.

What changes is the diagnostic client. In a classic workshop setup, the diagnostic client was a human with a laptop and a CAN adapter. In an AAOS-SDV architecture, the diagnostic client is an automated orchestrator running on Android, issuing HTTP requests to a SOVD CDA, which translates to UDS. Your ECU firmware doesn't know the difference. It sees the same UDS bytes either way.

The quality bar changes, though. A manual diagnostic session is forgiving — if a step fails, the engineer retries it. An automated OTA orchestrator is not forgiving. The session state machine needs to be correct. The security access protocol needs to handle sequence violations properly. The programming sequence needs to complete or fail cleanly, with no half-written flash state.

That's what the validation toolchain is for.


The practical starting point

If you're building ECU firmware for an SDV program that involves AAOS-SDV:

Get UDS right first. All 17 services, correct session gating, correct NRC codes. Run a robustness campaign. Fix the NRC mismatches before you worry about the transport layer.

Add DoIP. If your ECU will be on vehicle Ethernet — likely for any new-architecture program — DoIP is the diagnostic transport. The UDS core is unchanged.

Generate the SOVD CDA. Add --sovd to your codegen invocation. The output sovd_cda.json advertises your ECU's capabilities to AAOS-SDV diagnostic clients, Eclipse SDV tooling, and TestLab's SovdTester transport.

Automate your OTA sequence as a TestLab campaign. Run it on every firmware commit against the virtual ECU. By the time you're testing on hardware, the protocol bugs are gone.

AAOS-SDV going open source is significant for the Android and SoC integration side. For ECU firmware engineers, the requirements on your firmware haven't changed — the client side just got more automated, more service-oriented, and less forgiving of protocol defects.

Xaloqi EDS is open-source (GPL v2) for the runtime. Xaloqi TestLab is €990/yr. Xaloqi Cloud runs at €19–149/mo. See pricing →