OEM Vehicle Integration


Introduction


Some functionality in AGA is prepared to be modified for different OEMs and vehicles. This page describes how to configure and implement vehicle specific parts. The pattern that is used throughout the AGA southbound interface is interfaces and factory classes.

Mandatory implementations


Vehicle signal integration


One of the main focuses of AGA has been to expose vehicle functionality and data to third party applications and service providers. This is done by lifting signals from the vehicle buses to the AGA API. This section will try to explain how to do this by going into two different cases.

  1. Connect to an existing AGA signal
  2. Define a new OEM specific signal

Connect to an existing AGA signal


As an example we will look at one of the most commonly used signals, the speed. When you have gone through this step by step guide you will have hooked up the existing vehicle signal to AGA and the data will be exposed through the application API.

AGA expects a certain format on its internal data and this is specified in the file SignalsConfigurationImpl.java located in the git repo named vil (Vehicle Interface Layer). All signals configured in the AGA platform is configured here. Configured data per signal consists of:

  • Signal id. Unique 16 bit number identifying the signal
  • Is the signal continues or discrete
  • Human readable name and description
  • How often in milliseconds can a developer expect the signal
  • Unit of measure
  • Data format to the third party developer
  • Min and max value
  • Resolution of the data
  • Discrete signals will also have a list of allowed values with human readable names

For this example we will find the signal “Wheel based speed”. The configuration gives us the premises for our integration. Wheel based speed shall have a signal id of 0x0140 be sent as AGAs internal float representation in the range 0 to 300 km/h. Resolution is expected to be 1/256 but is based on the vehicle bus signal that we will expose.

With this information at hand it is possible to start the integration work. All signals pass through and are integrated in the VilSignalProxyImpl.java (also in the vil repo). This java class creates a VilSignalNode (located in the same package) and connects it. The implementation for VilSignalNode defaults to expose the platform via a network connected SDP server node. Reason for this is that the platform works with the simulator out of the box.

To change this one you simply have to change the implementation of the VilSignalNode. The easiest way is to integrate an external bus is to create a separate thread in the constructor. This thread in its turn calls the inherited method provide with all signal ids that should be lifted from the bus. In our example case the wheel based speed with id 0x0140. When the provide method is called the AGA system is ready to receive speed data.

At this point it is time to lift in the code that reads data from the vehicle bus. When new data is received it must be converted to the configured format. In the speed case we found out that it should be an AGA float (SCSFloat in sdp repo). To notify all subscribing applications about the new speed just call the inherited method send with the arguments signal id (0x0140) and the SCSFloat object containing the speed value.


Define new OEM specific signal


Compared to the previous example this one contains a signal NOT configured in the standard AGA installation. To be able to integrate a new signal one have to add the configuration self. OEMs are allowed to add their own signals in the signal id range 0xE000 to 0xEFFF (4096 signals).

As an example we will add a signal for the OEM specific vehicle data “bucket angle”. Open up the file SignalConfigurationImpl.java like in the previous example and add a configuration.

​CONFIG_INFO.put(0xE0000,
    new ContinuosSignalInfo("Bucket angle", "Signal description", 100, “DEGREES", DataType.FLOAT, 0, 360, 0.1));

With this configuration in place it is possible to follow the normal steps for adding an already existing AGA signal in previous example


Alternative signal integration


When one lifts signals from the vehicle busses one usually changes the VilSignalNode (found in the vil git repository) and add provides for new signal ids. An alternative version to do this is to let an Android service running in application context provide signals for other applications to listen to. The biggest benefit of doing this is that one can have a built ROM and just install new versions of this service application to grow in functionality during development. For this to be secure the application needs to be signed with a certificate allowing data writes in the policy manager internally in AGA.

What the application typically does is to request an AutomotiveManager when staring up. This is the exact same way you do when writing ordinary Automotive Grade Android applications. When the application has an instance of the automotive manager it calls provide for all the signals ids that should be provided. After this is done everything is setup for the service to start supplying data on those particular ids. This is done using the send method on the automotive manager.

For production deployment it is advices to move the implementation in the application to the VilSignalNode. This will increase performance because messages does not have to cross the IPC barrier between system and application layer more than once. When converting the code to use the proper vehicle interface layer the step to fetch automotive manger can be ignored. Implementation in VilSignalNode instead sends provides and data directly to the vehicle interface proxy.

Here follows an example of an application providing data. Main things to look at in this code example are the run function that is a simple while loop sending data once every 10 milliseconds and the onCreate method that initializes the automotive manager reference.

import android.app.Notification;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.swedspot.automotive.AutomotiveManager;
import android.swedspot.automotiveapi.AutomotiveSignal;
import android.swedspot.automotiveapi.AutomotiveSignalId;
import android.swedspot.automotiveapi.AutomotiveSignalInfo;
import android.swedspot.scs.data.SCSFloat;
import android.util.Log;

public class AGAVilIntegrationService extends Service implements Runnable {

    private static final int YOUR_SIGNAL_ID_HERE = 320;
    private IBinder binder;
    private AutomotiveManager manager;
    private Thread dataExtractionThread;
    private boolean isRunning;
    private int i;

    public AGAVilIntegrationService() {
        binder = new LocalBinder();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    @Override
    public void run() {
        isRunning = true;

        // Tell the AGA platform that this is a provider of a signal
        manager.provide(YOUR_SIGNAL_ID_HERE);

        while (isRunning) {
            // Get extracted data from sensors. In this example we assume we only have one sensor that
            // we are reading from.
            float dataFromSensor = getDataFromSensor();

            // Get information regarding the signal you want to send
            AutomotiveSignalInfo signalInformation = manager.getSignalInformation(AutomotiveSignalId.FMS_WHEEL_BASED_SPEED);

            // Create the signal and set the data value
            AutomotiveSignal automotiveSignal = new AutomotiveSignal(
                    AutomotiveSignalId.FMS_WHEEL_BASED_SPEED, // The signal id (numbers such as 320 works as well)
                    new SCSFloat(dataFromSensor), // The data from the sensor
                    signalInformation.getUnit() // The unit, such as kilometers per hour or centimeters.
            );

            // Send the sensor data into the AGA platform
            manager.send(automotiveSignal);
            try {
                Thread.sleep(10);
            } catch (InterruptedException ignore) { }
        }

        // Tell the AGA platform that you are no longer providing a specific signal
        manager.unprovide(YOUR_SIGNAL_ID_HERE);
    }

    /**
     * This is an extraction method, this is were you are supposed to grab the data from your sensors
     * and return it.
     */
    private float getDataFromSensor() {
        return (float) Math.sin(i++ * 0.01);
    }

    public class LocalBinder extends Binder {
        public AGAVilIntegrationService getAGAVilIntegrationServiceInstance() {return AGAVilIntegrationService.this;}
    }

    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(707, new Notification());

        // Get the automotive manager from the system. This will return an interface with an SDP node inside the
        // AGA proxy. This manager is unique for each application.
        manager = (AutomotiveManager) getSystemService(AUTOMOTIVE_SERVICE);

        dataExtractionThread = new Thread(this);
        dataExtractionThread.setDaemon(true);
        dataExtractionThread.start();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        isRunning = false;
        try {
            dataExtractionThread.join();
        } catch (InterruptedException ignore) { }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }
}

​And also an example of thread that can be placed in the VilSignalNode cunstructor for production deployment. This example is just a while loop sending data as fast as possible. OEM implementation should probably be triggered periodically or on external event from i.e. CAN bus.

​Thread vehicleDataExtractionThread = new Thread(new Runnable() {
    private final static int VEHICLE_DATA_SIGNAL_ID = 777;
    private final boolean running = true;

    @Override
    public void run () {
        vilNode.provide(VEHICLE_DATA_SIGNAL_ID);

        while (running) {
            byte[] data = extractVehicleData();
            vilNode.send(VEHICLE_DATA_SIGNAL_ID, data);
        }

        vilNode.unprovide(VEHICLE_DATA_SIGNAL_ID);
    }
});
vehicleDataExtractionThread.setDaemon(true);
vehicleDataExtractionThread.start();

Signal policies


When an application is started on AGA, internally a PolicyEnforcement object will be fetched from the system. On this object the method getAuthorizations is called. As an argument the certificate of the application is supplied. Based on the certificate the OEM can return an Authorizations object with policy information about signals with the given certificate. Authorizations is an interface located in package com.swedspot.vil.policy and has two methods.
isReadAllowed(signal id) and isWriteAllowed(signal id).

Steps to implement policies for OEM:

  1. Implement the Authorizations interface to support the OEM use case.
  2. Implement the PolicyEnforcement interface as singleton and return implemented Authorizations objects based on certificate.
  3. Return the new PolicyEnforcement implementation in PolicyEnforcementFactory#getInstance()

Optional implementations


Extend signal set


The easiest way forward is to copy/modify the com.swedspot.vil.configuration.SignalsConfigurationImpl class from the vil repository.

Do not remove existing signals defined for AGA and FMS, but extend the signal set with an OEM set. It is recommended that you as an OEM contact the AGA project to get a range.

When you have added your signal set, please contribute back to the community and have the set included by the AGA SDK off the shelf (if okay).

Driver distraction calculations


The AGA platform has a predefined interface to integrate Driver Distraction into the platform, the interface is located in the vil repository and is named: com.swedspot.vil.distraction.DriverDistraction.

If the OEM wants to implement logic for Driver Distraction, the easiest way is to either re-implement com.swedspot.vil.distraction.impl..DriverDistractionImpl or create e.g. com.swedspot.vil.distraction.impl.OEMDriverDistractionImpl and update the factory com.swedspot.vil.distraction.impl.DriverDistractionImpl.DriverDistractionFactory.

Hardware buttons


The AGA platform has a predefined interface to integrate hardware buttons into the platform, the interface is located in the vil repository and is named: com.swedspot.vil.keys.HardwareButtonController
Mappings for all default Android buttons exists in the enum com.swedspot.vil.keys.Key.

If the OEM wants to integrate e.g. buttons on a steering wheel the easiest way is to extend the abstract class: com.swedspot.vil.keys.impl.AbstractHardwareButtonController. This class implements the HardwareButtonController interface and handles default behaviour for listeners within the AGA platform. An OEM implementation needs to be singleton.

Example code:

public final class OEMButtonController extends AbstractHardwareButtonController {

    private static HardwareButtonController instance;

    static {
        instance = new OEMButtonController();
    }

    private OEMButtonController() {
    // TODO add OEM code to integrate buttons
    // TODO on incoming button event call keyPressed(Key) or keyLongPressed(Key)
    }

    public static HardwareButtonController getInstance() {
        return instance;
    }
}

When the implementation is done it can be activated by returning the instance in com.swedspot.vil.keys.HardwareButtonControllerFactory#getInstance()

OEM Vehicle ROM Build Script


References

OEM AGA Artifacts


Modify the function explodeAgaJarFiles() in the build script MakeCustomAgaBuild442.sh.

Currently the script downloads three AGA source code artifacts and explodes these into the Android source tree:

As you are doing implementations and integrations of your requirements and environments, three new OEM source code artifacts shall be created that the build script can explode into the Android source tree.

Proprietary Drivers


Modify the function explodeProprietaryBinaries() in the build script MakeCustomAgaBuild442.sh.

The function shall extract all needed drivers for your OEM platform.

Under:

################################################################################
#
# This is the main() program
#
################################################################################

Remove not needed architectures if not needed (or leave as is) and remove from all the functions in the script.

Create your own OEM architecture build:

    "acme-oem-rom" )
            BUILD_DIRECTORY=${HOME}/AGA_BUILD_acme-oem-rom
            checkPrerequisites
            downloadAndroidSource "rom"
            setupEnvironment "acme-oem-rom"
            explodeAgaJarFiles
            explodeProprietaryBinaries "acme-oem-rom"
            explodeAgaService
            addMedia
            executeBuild "rom"
            createImageZip "acme-oem-rom"
            ;;

Finally modify each function that takes “acme-oem-rom” as an argument.