Vehicle data is accessed through an Android service. Your application binds with the VehicleManager which handles reading fresh data from the vehicle and sending commands.

This document describes the interface for the OpenXC library version 6.0.

You must first bind with the VehicleManager before you can receive any vehicle data.

Request to bind with the service in the normal fashion:

Intent intent = new Intent(this, VehicleManager.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

This will require you to import the VehicleManager class from OpenXC:

import com.openxc.VehicleManager;

Then, in your onServiceConnected() grab the service handle and cast it to the correct type:

VehicleManager vehicle;
private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established
    public void onServiceConnected(ComponentName className, IBinder service) {
        vehicle = ((VehicleManager.VehicleManagerBinder)
                service).getService();
    }

    // Called when the connection with the service disconnects unexpectedly
    public void onServiceDisconnected(ComponentName className) {
        vehicle = null;
    }
};

Once the service is bound and you have a connection to it in your activity, you can start sending and receiving messages with the vehicle interface.

The VehicleMessage is the parent of all message sent to and from the vehicle interface. You can register to receive asynchronous updates for anything from all receive messages to only those matching a certain key. All of the examples here show how to receive updates for more specific subtypes, but you do have the option of interfacing directly with VehicleMessages.

If the attached VI is sending low-level CAN messages, you can receive them in your Android application.

private VehicleMessage.Listener mListener = new VehicleMessage.Listener() {
    @Override
    public void receive(final VehicleMessage message) {
    }
};

// Elsewhere in your activity, register this listener to receive all CAN
// messages from the VI
mVehicleManager.addListener(CanMessage.class, mListener);

You can also configure this listener to only receive CAN messages matching a certain message ID:

// Register the listener to receive CAN messages from bus 1 with the ID of 42.
mVehicleManager.addListener(new CanMessage(1, 42, null), mListener);

You can send a diagnostic request and wait for a response:

// Send a mode 3 request to message ID 2 on bus 1 wait for a response.
VehicleMessage response = mVehicleManager.request(new DiagnosticRequest(1, 2, 3));
if(response != null) {
    DiagnosticResponse diagnosticResponse = response.asDiagnosticResponse();
}

You can also register a receiver for the response so this function is non-blocking:

private VehicleMessage.Listener mListener = new VehicleMessage.Listener() {
    @Override
    public void receive(final VehicleMessage message) {
        // TODO
    }
};

// Elsewhere in your activity, make the request and register the listener to
// receive the response:
mVehicleManager.request(new DiagnosticRequest(1, 2, 3), mListener);

You can also send the request without caring about responses:

mVehicleManager.send(new DiagnosticRequest(1, 2, 3));

The commands defined in the OpenXC message format that are supported by your VI firmware can be sent using a Command message type.

To send a command and wait to receive a response:

VehicleMessage response = mVehicleManager.request(new Command(CommandType.VERSION);

There is also a non-blocking version of the request method that takes a callback argument.

To send a command but return immediately, without waiting for a response:

mVehicleManager.send(new Command(CommandType.VERSION);

For commands that don't require any data in the request you can use this shortcut:

mVehicleManager.requestCommandMessage(CommandType.VERSION);

The VehicleManager has a couple of the supported VI commands built into the API - the firmware version and device ID.

String version = mVehicleManager.getVehicleInterfaceVersion();
String deviceId = mVehicleManager.getVehicleInterfaceDeviceId();

A pre-defined set of simple vehicle messages, the official OpenXC signals is also exposed in the Android library as higher level Measurement objects. These work similarly to VehicleMessages but include more metadata and checks to make sure the data is valid.

To query for a measurement synchronously, e.g. the speed of the vehicle, import the com.openxc.measurements.VehicleSpeed. You'll also need to import the necessary exceptions:

import com.openxc.measurements.VehicleSpeed;
import com.openxc.measurements.UnrecognizedMeasurementTypeException;
import com.openxc.remote.VehicleServiceException;

You can then create a VehicleSpeed object and get the speed from the vehicle object.

try {
    VehicleSpeed measurement = (VehicleSpeed) vehicle.get(VehicleSpeed.class);
} catch(NoValueException e) {
    Log.w(TAG, "The vehicle may not have made the measurement yet");
} catch(UnrecognizedMeasurementTypeException e) {
    Log.w(TAG, "The measurement type was not recognized");
}

If you want to access a different measurement, cast the measurement to any of the available measurement objects in the API.

If you need to be kept up-to-date on any vehicle measurement, you can also register with the service to be notified of updates.

VehicleSpeed.Listener listener = new VehicleSpeed.Listener() {
    public void receive(Measurement measurement) {
        final VehicleSpeed speed = (VehicleSpeed) measurement;
        // do stuff with the measurement
    }
};
vehicle.addListener(VehicleSpeed.class, listener);

The parent Measurement class provides a standard interface for all measurements, starting with a way to retrieve its value:

VehicleSpeed measurement = vehicle.get(VehicleSpeed.class);
KilometersPerHour value = measurement.getValue();

Occasionally a measurement will not have been received from the vehicle interface yet - it could be a malfunction, or you're just checking too early for an infrequently updated measurement:

try {
    VehicleSpeed measurement = vehicle.get(VehicleSpeed.class);
    // do stuff with it
} catch(NoValueException e) {
    Log.w(TAG, "The vehicle may not have made the measurement yet");
}

You can check the age of a measurement to determine how long ago the state was actually reflected in the physical vehicle:

if(measurement.getAge() < 10) {
    // only if age is less than 10 seconds
}

The range of every measurement is defined in the class, and can be queried programmatically if you need to figure out the current value's relative position:

range = measurement.getRange();
int percentage = measurement.getValue().doubleValue() / range;

The Android framework includes built-in support for location information - most devices support determining position based on CellID, GPS and Wi-Fi. OpenXC's latitude, longitude and vehicle speed are exposed through this interface using a vehicle location provider. This functionality is provided by Android primarily for testing, but it's also a useful way to inject location data from sources other than the built-in sensors. It works well with Bluetooth GPS receivers, too.

If your app already uses the Android location framework, with a very small change you can increase the accuracy by taking advantage of the GPS receiver built into many vehicles (including the 2012 Ford Focus, even those without a navigation system).

Add the ACCESS_FINE_LOCATION and ACCESS_MOCK_LOCATION permission to your app's AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />

Open a connection to the VehicleManager as described earlier in "Service Binding." Then access user location in the traditional Android fashion - data from the LocationManager.GPS_PROVIDER and VehicleManager.VEHICLE_LOCATION_PROVIDER providers will be coming from OpenXC. You may want to continue to use the GPS_PROVIDER so your app can support switching from built-in GPS to the vehicle's receiver.

locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);

OpenXC Android Library Data Flow

Data Flow

Vehicle data is first received by a physical interface, e.g. USB (#1 in the figure), in an Android process running the VehicleService. The data processed and encapsulated in Java objects as a part of a DataPipeline (#2) before being propagated to all client applications (#3).

The client applications (in a separate process) receive the data in an instance of the VehicleManager service (#4) which notifies any activities that have previously registered their interest in vehicle data (#5).

There are a few sequence diagrams available in the openxc-android docs directory.

Remote and Local Services

Android exposes many of its system level functions in the form of services. This is a useful paradigm for long-running components of an application and perfect for OpenXC, which needs to maintain a connection to the vehicle to continue receiving new measurements.

Services can run either inside an application's address space or as an external process; OpenXC uses both.

The core of the system is the VehicleService, which runs in a separate process to any app. Since only one application at a time can connect to a USB device , this service controls access to the USB vehicle interface. From here, new data from the vehicle (or other sources, as you'll see) is propagated to any applications using the OpenXC library. The benefit is that multiple OpenXC-enabled apps can run on the same system, simultaneously.

Application developers use a second, in-process service - the VehicleManager. This service provides the interface that you'll use when developing your app. Behind the scenes, it binds to the VehicleService (running in another process) and communicates with it using the Android Interface Definition Language. The benefit of using the VehicleManager in-process is that developers don't need to care about any of this - it's all abstracted away.

Data Pipeline

The VehicleManager uses the concept of a pipeline to funnel data from "sources" to "sinks." This is a one-way pipeline.

Source

A data source injects new measurements into the OpenXC vehicle service. Sources can be added by the internal vehicle service itself (e.g. the source that connects to a USB device), the Enabler (e.g. a Bluetooth device source) or an app (e.g. the trace source, as in the tutorial).

At whatever level you add the source, its data is made available to all other OpenXC apps in the system. This is because even when a source is added to the VehicleManager, it ferries incoming measurements back to the VehicleService. Client apps can create and add their own custom sources, and when the client app is destroyed by Android the source will no longer send any data.

The ApplicationSource is a special case - it's the method by which data from an app's custom source is propagated back to the VehicleService and then all other apps.

Besides the VehicleInterface subclasses, there is one built-in source for developers:

Sink

A data sink receives every new measurement put into the pipeline by the sources. It receives the data in its base form, a VehicleMessage.

Data sinks handle the core of the library's asynchronous notification system, too. The VehicleManager always adds an instance of the MessageListenerSink, which keeps tabs on which applications want to be notified of changes in measurements.

The built-in sinks for application developers are:

  • FileRecorderSink - records all incoming vehicle measurements to a file on the internal storage that can later be used as a trace file.
  • UploaderSink - send all incoming vehicle measurements to a URL via HTTP.

Remote Service

The remote process VehicleService also uses a pipeline, but it's not accessible to application developers. The sources for this pipeline are the active vehicle interface (e.g. Bluetooth, USB or Network) and:

  • ApplicationSource - this source has a special callback that the VehicleManager instances in all OpenXC apps use to send any new measurements they create back to the VehicleService. The purpose is that e.g. a trace data source or Bluetooth source instantiated in an application can share its data with all other apps in the system.

The VehicleService only uses two sinks:

  • RemoteCallbackSink - funnels vehicle data asynchronously with AIDL to applications.
  • VehicleLocationProvider - pushes location and speed information from the vehicle into the normal Android location framework.

Application developers don't need to worry about these, but it's good to know how data flow through the system.

Enabler

The OpenXC Enabler app is primarily for convenience, but it also increases the reliability of the system by handling background tasks on behalf of client applications.

The Enabler provides a common location to control which data sources and sinks are active, e.g. if the a trace file should be played back or recorded. It's preferable to be able to change the data source on the fly, and not have to programmatically load a trace file in any application under test.

With the enabler installed, the VehicleService is also started automatically when the Android device boots up. A simple data sink like a trace file uploader can start immediately without any user interaction. This behavior probably isn't desirable for a phone-based OpenXC system, but makes more sense when using a tablet or other Android device that is permanently installed in the vehicle.

As a developer, you can also appreciate that because the enabler takes care of starting the VehicleService, you don't need to add much to your application's AndroidManifest.xml - just the VehicleManager service.