Introduction to Custom Car Services in AAOS
Android Automotive OS (AAOS) provides a powerful and flexible platform for in-vehicle infotainment and control. At its core, AAOS leverages a dedicated set of system services known as ‘Car Services’ to abstract vehicle-specific hardware and expose its functionalities to applications. While AAOS offers a rich set of pre-defined Car Services like CarPropertyManager, CarSensorManager, and others, advanced automotive solutions often require custom services to expose unique vehicle functionalities or integrate with proprietary hardware through the Vehicle Hardware Abstraction Layer (VHAL).
This article delves into the intricacies of developing a custom Car Service in AAOS, with a particular focus on how to establish robust integration with the Vehicle HAL. We will explore the architecture, define custom properties, implement the service, and demonstrate client application interaction, empowering you to extend AAOS’s capabilities to meet specific automotive requirements.
Understanding the AAOS Vehicle HAL Architecture
The Vehicle HAL is the bridge between the Android framework and the underlying vehicle hardware. It defines a set of standard vehicle properties (e.g., speed, gear, HVAC controls) and provides an interface for the Android Car Service to read from and write to these properties. For advanced use cases, developers may need to introduce custom vehicle properties or extend existing ones to expose unique hardware features not covered by the standard VHAL interface.
Key components in this architecture include:
- Vehicle HAL (VHAL): A set of HIDL/AIDL interfaces (`hardware/interfaces/automotive/vehicle/`) that abstract vehicle hardware. Implementations are provided by OEMs.
- CarService: The central AAOS service that manages other car-related services and acts as a gateway for applications to access vehicle properties via `CarPropertyManager`.
- CarPropertyManager: An API provided by the CarService for applications to read and write standard vehicle properties.
- Custom Car Service: Your application-specific service designed to expose custom vehicle functionalities, potentially interacting with VHAL directly or through a proxy.
Defining Custom Vehicle Properties (Conceptual)
While direct modification of the VHAL implementation requires OEM-level access and AOSP compilation, understanding how custom properties are defined is crucial. New vehicle properties are typically added to the VHAL definition files (e.g., `types.hal` and `Vhal.aidl` in `hardware/interfaces/automotive/vehicle/`).
For instance, to add a custom property for a ‘Tire Pressure Monitoring System (TPMS) Calibration Status’, you might conceptually define it like this:
// In a custom types.hal or similar definition file within VHAL source tree
enum VehicleProperty : int38_t {
// ... existing properties
TPMS_CALIBRATION_STATUS = 0x21100201, // Vendor property ID example
};
enum TpmsCalibrationStatus : int32_t {
NOT_CALIBRATED = 0,
CALIBRATING = 1,
CALIBRATED_SUCCESS = 2,
CALIBRATED_FAILED = 3,
};
This new property would then need to be implemented in the OEM’s VHAL daemon. For our custom Car Service, we will simulate managing such a property within our service, assuming the underlying VHAL *could* provide it or that our service is the canonical source for this custom data.
Building Your Custom Car Service
Let’s create a custom service named `CustomVehicleControlService` that will expose a simulated TPMS calibration status.
1. Define the AIDL Interface
First, define the API for your custom service using AIDL (Android Interface Definition Language). Create `src/main/aidl/com/example/vehiclecontrol/ICustomVehicleControlService.aidl`:
// ICustomVehicleControlService.aidl
package com.example.vehiclecontrol;
interface ICustomVehicleControlService {
int getTpmsCalibrationStatus();
void setTpmsCalibrationStatus(int status);
// Example: Registering a callback for status changes
void registerTpmsStatusCallback(ITpmsStatusCallback callback);
void unregisterTpmsStatusCallback(ITpmsStatusCallback callback);
}
And the callback interface `src/main/aidl/com/example/vehiclecontrol/ITpmsStatusCallback.aidl`:
// ITpmsStatusCallback.aidl
package com.example.vehiclecontrol;
oneway interface ITpmsStatusCallback {
void onTpmsStatusChanged(int status);
}
2. Implement the Custom Car Service
Next, create the `CustomVehicleControlService.java` class that extends `android.app.Service` and implements the generated `ICustomVehicleControlService.Stub`.
// CustomVehicleControlService.java
package com.example.vehiclecontrol;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class CustomVehicleControlService extends Service {
private static final String TAG = "CustomVehicleControlService";
private int mTpmsCalibrationStatus = 0; // 0: NOT_CALIBRATED
private final RemoteCallbackList<ITpmsStatusCallback> mCallbacks = new RemoteCallbackList<>();
private final ScheduledExecutorService mScheduler = Executors.newSingleThreadScheduledExecutor();
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "CustomVehicleControlService created.");
// Simulate VHAL interaction or sensor updates
mScheduler.scheduleAtFixedRate(this::simulateStatusChange, 5, 10, TimeUnit.SECONDS);
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "Binding to ICustomVehicleControlService.");
return mBinder;
}
private final ICustomVehicleControlService.Stub mBinder = new ICustomVehicleControlService.Stub() {
@Override
public int getTpmsCalibrationStatus() {
Log.d(TAG, "Getting TPMS calibration status: " + mTpmsCalibrationStatus);
return mTpmsCalibrationStatus;
}
@Override
public void setTpmsCalibrationStatus(int status) {
Log.d(TAG, "Setting TPMS calibration status to: " + status);
if (mTpmsCalibrationStatus != status) {
mTpmsCalibrationStatus = status;
notifyTpmsStatusChanged(status);
}
}
@Override
public void registerTpmsStatusCallback(ITpmsStatusCallback callback) {
if (callback != null) {
mCallbacks.register(callback);
Log.d(TAG, "Registered TPMS status callback.");
}
}
@Override
public void unregisterTpmsStatusCallback(ITpmsStatusCallback callback) {
if (callback != null) {
mCallbacks.unregister(callback);
Log.d(TAG, "Unregistered TPMS status callback.");
}
}
};
private void notifyTpmsStatusChanged(int newStatus) {
int i = mCallbacks.beginBroadcast();
while (i > 0) {
i--;
try {
mCallbacks.getBroadcastItem(i).onTpmsStatusChanged(newStatus);
} catch (RemoteException e) {
Log.e(TAG, "Error notifying callback", e);
}
}
mCallbacks.finishBroadcast();
}
private void simulateStatusChange() {
// In a real scenario, this would read from VHAL or actual sensor
int newStatus = (int) (System.currentTimeMillis() / 1000 % 4); // Cycle 0-3
Log.d(TAG, "Simulating TPMS status change to: " + newStatus);
setTpmsCalibrationStatus(newStatus);
}
@Override
public void onDestroy() {
super.onDestroy();
mScheduler.shutdownNow();
mCallbacks.kill();
Log.i(TAG, "CustomVehicleControlService destroyed.");
}
}
3. Register the Service in AndroidManifest.xml
Add the service declaration to your `AndroidManifest.xml`. Crucially, specify the `android.car.permission.BIND_CAR_SERVICE` permission and the action matching your AIDL interface name.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.vehiclecontrol">
<uses-permission android:name="android.car.permission.BIND_CAR_SERVICE" />
<uses-permission android:name="android.permission.INTERNET" /> <!-- Example -->
<application
...
>
<service
android:name=".CustomVehicleControlService"
android:exported="true"
android:permission="android.car.permission.BIND_CAR_SERVICE">
<intent-filter>
<action android:name="com.example.vehiclecontrol.ICustomVehicleControlService" />
</intent-filter>
</service>
...
</application>
</manifest>
Ensure your application targets a system image with `android.car.permission.BIND_CAR_SERVICE` available (i.e., an AAOS build). For client apps, they will need appropriate permissions to interact with your service.
Integrating with Vehicle HAL (Advanced)
In a real-world scenario, `CustomVehicleControlService` wouldn’t just simulate status changes. It would interact with the `CarPropertyManager` to read/write standard VHAL properties or, for truly custom properties, integrate with an underlying VHAL implementation (often through a JNI bridge to C++ VHAL services or directly if the custom VHAL property is exposed via `CarPropertyManager` as a vendor property).
If you’ve defined a custom VHAL property like `TPMS_CALIBRATION_STATUS` within the AOSP source and implemented it in the VHAL daemon, your `CustomVehicleControlService` could use `CarPropertyManager` to interact with it:
// Example of how CustomVehicleControlService would interact with CarPropertyManager
// (This requires the custom property ID to be recognized by CarPropertyManager)
import android.car.Car;
import android.car.VehiclePropertyIds;
import android.car.hardware.CarHvacManager;
import android.car.hardware.CarPropertyConfig;
import android.car.hardware.CarPropertyEvent;
import android.car.hardware.CarPropertyManager;
import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback;
// ... inside CustomVehicleControlService ...
private CarPropertyManager mCarPropertyManager;
private Car mCar;
@Override
public void onCreate() {
super.onCreate();
// ... other onCreate logic ...
mCar = Car.createCar(this, null);
if (mCar == null) {
Log.e(TAG, "Failed to create Car instance");
return;
}
mCarPropertyManager = (CarPropertyManager) mCar.getCarManager(Car.PROPERTY_SERVICE);
if (mCarPropertyManager == null) {
Log.e(TAG, "Failed to get CarPropertyManager");
return;
}
// Register for our custom property, assuming it's available via CarPropertyManager
// VEHICLE_PROPERTY_TPMS_CALIBRATION_STATUS would be a custom defined constant
int CUSTOM_TPMS_PROPERTY_ID = 0x21100201; // Match ID from VHAL definition
if (mCarPropertyManager.is//// (This requires the custom property ID to be recognized by CarPropertyManager)
import android.car.Car;
import android.car.VehiclePropertyIds;
import android.car.hardware.CarHvacManager;
import android.car.hardware.CarPropertyConfig;
import android.car.hardware.CarPropertyEvent;
import android.car.hardware.CarPropertyManager;
import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback;
// ... inside CustomVehicleControlService ...
private CarPropertyManager mCarPropertyManager;
private Car mCar;
@Override
public void onCreate() {
super.onCreate();
// ... other onCreate logic ...
mCar = Car.createCar(this, null);
if (mCar == null) {
Log.e(TAG, "Failed to create Car instance");
return;
}
mCarPropertyManager = (CarPropertyManager) mCar.getCarManager(Car.PROPERTY_SERVICE);
if (mCarPropertyManager == null) {
Log.e(TAG, "Failed to get CarPropertyManager");
return;
}
// Register for our custom property, assuming it's available via CarPropertyManager
// VEHICLE_PROPERTY_TPMS_CALIBRATION_STATUS would be a custom defined constant
int CUSTOM_TPMS_PROPERTY_ID = 0x21100201; // Match ID from VHAL definition
if (mCarPropertyManager.is