Android IoT, Automotive, & Smart TV Customizations

Developing Custom CAN Protocol Drivers for AAOS: A Deep Dive into HAL Implementation

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction

Android Automotive OS (AAOS) is rapidly becoming the standard for in-vehicle infotainment systems. While AAOS provides a robust framework for managing vehicle functions through its Vehicle Hardware Abstraction Layer (VHAL), integrating proprietary or highly specialized CAN bus protocols often requires developing custom drivers. This article provides an expert-level guide to understanding, designing, and implementing custom CAN protocol drivers within the AAOS VHAL framework, enabling seamless communication between Android and non-standard vehicle ECUs.

Understanding AAOS and the Vehicle HAL

AAOS leverages the VHAL to abstract away vehicle-specific hardware implementations, presenting a standardized interface to the Android application layer. The VHAL defines a set of generic vehicle properties (e.g., speed, gear, HVAC settings) that applications can read from or write to. These properties are managed by a VHAL implementation, typically a C++ service, which translates Android requests into vehicle-specific bus commands (like CAN, LIN, Ethernet) and vice versa.

However, many automotive systems utilize custom CAN protocols for specific sensors, actuators, or proprietary features not covered by standard VHAL properties. Direct access to the CAN bus from user space is generally discouraged for security and stability reasons. The VHAL offers the correct abstraction point to bridge this gap, allowing us to define and manage custom properties that encapsulate our unique CAN message structures.

Why Custom CAN Protocols?

  • Proprietary ECUs: Many manufacturers use custom CAN messages for unique features or legacy systems.
  • Specialized Sensors: Integrating advanced sensors (e.g., custom LiDAR, radar, or biometric sensors) that transmit data via non-standard CAN frames.
  • Optimized Communication: Tailoring CAN message formats for specific data efficiency or real-time requirements.

CAN Bus Fundamentals Revisited

The Controller Area Network (CAN) bus is a message-based protocol designed for robust communication in automotive environments. Key characteristics include:

  • Message-Based: Data is transmitted in short messages (frames).
  • Arbitration: Messages are prioritized by their ID; lower ID wins arbitration.
  • Frame Types: Data frames, remote frames, error frames, overload frames. We primarily focus on data frames.
  • Data Field: Up to 8 bytes of data per frame.

For custom protocols, the crucial aspects are how your specific ECU maps data to CAN IDs and the byte-level encoding within the data field.

Designing Custom VHAL Properties

The first step in integrating a custom CAN protocol is to define how its data will be represented as VHAL properties. This involves:

  1. Choosing a Unique Property ID: Vehicle properties are identified by integer IDs. For custom properties, use IDs in the vendor-specific range (VehicleProperty.VENDOR_EXT_START and VehicleProperty.VENDOR_EXT_END).
  2. Defining Data Type: Specify the data type (e.g., INT32, FLOAT, BYTE_ARRAY, INT32_VEC) that best represents the CAN data.
  3. Setting Access and Change Modes: Determine if the property is read-only (READ_ONLY), read-write (READ_WRITE), or if it supports subscription (ON_CHANGE, CONTINUOUS).
  4. Mapping CAN Frames to Properties: Decide how specific CAN IDs and their data bytes will translate into your defined VHAL property’s value.

Here’s an example of defining a custom property in a theoretical VehicleProperty.java extension or a similar vendor-specific constants file:

// In vendor/your_company/automotive/hal/vehicle/VhalProperties.java (or similar)interface VehicleProperty {    // ... existing properties ...    /**    * Custom CAN Sensor Data Property.    * ID: 0x11000000 + 1    * Type: VehiclePropertyType.FLOAT    * Access: VehiclePropertyAccess.READ_ONLY    * Change Mode: VehiclePropertyChangeMode.ON_CHANGE    */    int CUSTOM_CAN_SENSOR_TEMP_FRONT_LEFT = 0x11000001;    /**    * Custom CAN Actuator Control.    * ID: 0x11000000 + 2    * Type: VehiclePropertyType.INT32    * Access: VehiclePropertyAccess.READ_WRITE    * Change Mode: VehiclePropertyChangeMode.ON_CHANGE    */    int CUSTOM_CAN_ACTUATOR_STATE = 0x11000002;}

Implementing the Custom VHAL Module

Your custom VHAL module will be a C++ service that implements the IVehicle interface. This is where the translation between AAOS property requests and raw CAN frames happens.

Directory Structure

Typically, custom HALs reside in vendor/your_company/automotive/hal/vehicle/ or similar, mirroring the Android source structure.

Key Components

  1. MyCanHal.h: Defines your HAL class, inheriting from IVehicle.
  2. MyCanHal.cpp: Contains the implementation of the IVehicle methods, plus your CAN communication logic.
  3. CAN Interface Layer: Abstraction for interacting with the physical CAN bus. For Linux-based systems like AAOS, socketcan is the preferred method.

Interfacing with socketcan

socketcan provides a standardized network socket interface to CAN devices in Linux. Ensure your kernel has CONFIG_CAN and CONFIG_CAN_RAW enabled, and your specific CAN hardware has a corresponding kernel driver (e.g., mcp251x for SPI-based CAN controllers, or USB-CAN adapter drivers).

First, configure the CAN interface. This is typically done during device boot or initialization scripts:

# Enable the CAN interface (e.g., can0)ip link set can0 up type can bitrate 500000 sample-point 0.8 ip link set can0 up

In your C++ HAL implementation, you’ll use standard socket programming:

// In MyCanHal.cpp or a dedicated CanDriver.cpp#include <linux/can.h>#include <linux/can/raw.h>#include <net/if.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <unistd.h>class CanDriver {public:    CanDriver(const std::string& ifname) : mSockFd(-1) {        mSockFd = socket(PF_CAN, SOCK_RAW, CAN_RAW);        if (mSockFd == -1) {            // Handle error        }        struct ifreq ifr;        strcpy(ifr.ifr_name, ifname.c_str());        if (ioctl(mSockFd, SIOCGIFINDEX, &ifr) == -1) {            // Handle error        }        struct sockaddr_can addr;        addr.can_family = AF_CAN;        addr.can_ifindex = ifr.ifr_ifindex;        if (bind(mSockFd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {            // Handle error        }    }    ~CanDriver() {        if (mSockFd != -1) {            close(mSockFd);        }    }    // Send a CAN frame    bool sendCanFrame(uint32_t id, const uint8_t* data, size_t len) {        struct can_frame frame;        frame.can_id = id;        frame.can_dlc = len;        memcpy(frame.data, data, len);        return write(mSockFd, &frame, sizeof(struct can_frame)) == sizeof(struct can_frame);    }    // Read a CAN frame (blocking or non-blocking)    bool readCanFrame(struct can_frame* frame) {        return read(mSockFd, frame, sizeof(struct can_frame)) == sizeof(struct can_frame);    }private:    int mSockFd;};

Implementing IVehicle Methods

Your MyCanHal class will override methods like get(), set(), and subscribe(). Within these, you’ll map the custom VHAL property IDs to your CAN driver logic.

// In MyCanHal.cpp#include <android/hardware/automotive/vehicle/2.0/IVehicle.h>#include <android/hardware/automotive/vehicle/2.0/types.h>#include <android/hardware/automotive/vehicle/2.0/VehicleProperty.h>#include

Android Mobile Specs & Compare Directory

Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!

Compare Devices Specs →
Google AdSense Inline Placement - Content Footer banner