Android Automotive: Build your first app
Authors: Thiruppathi Krishnan | Sreelakshmi S Dilip
Introduction
In our previous blog, we explored the key distinctions between mobile and automotive app development for Android. Now, let’s shift gears and dive into the practicalities of building your first Android app specifically designed for Android Automotive OS.
This hands-on tutorial will guide you through the entire process: from creating a brand new Android app to integrating it seamlessly within the Android Automotive build environment. We’ll also walk you through running the app on an emulator and delve into more advanced topics like accessing vehicle data (e.g., A/C status).
Setting Up the Environment
Ensure your system meets the requirements mentioned here.
- Install repo source control.
- Clone the Android Open Source Project(AOSP) repository and set the environment.
- Choose your target using lunch options. Build the source using make and launch the emulator using emulator &.
Detailed instructions are available here. Feel free to use the latest version; we used android 14.0.0_r20 for this guide.
Integration Methods
There are two main ways to integrate an app with Android Automotive:
- Adding the Complete Source Code: Embed your app’s code directly into the AOSP build system.
- Adding the Compiled APK File: Build your app separately, create an APK, and integrate it into the AOSP folder.
Method 1: Integrating Source Code (Detailed Steps)
1. Create a Sample Android Project:
This sample app is similar to 3rd party mobile applications. We will integrate it with AOSP source code and then test it in the Android Automotive Operating System(AAOS) emulator.
- Launch Android Studio and choose the Automotive template with No Activity.
- Name your project “HelloAAOS”, select your preferred language(Java, in our case) and minimum SDK.
- Add an empty activity with a “Hello world” text view or similar content.
2. Prepare a Folder in AOSP:
- In the AOSP source code, navigate to /packages/apps/Car.
- Duplicate an existing app folder (e.g calendar app) and rename it to “HelloAAOS”.
- Delete all files and folders except src, res and Android.bp. Delete the existing contents inside src and res folders and keep it empty.
3. Copy App Code and Resources:
- Copy the com folder under HelloAAOS/automotive/src/main/java to the AOSP project’s HelloAAOS/src folder.
- Do the same for the res folder under HelloAAOS/automotive/src/main/res.
- Finally, copy AndroidManifest.xml to the AOSP project’s HelloAAOS folder.
- In AndroidManifest.xml, make sure that the uses-feature tag is added with android.hardware.type.automotive for automotive functionality.
- Add the package name in the AndroidManifest.xml.
<manifest xmlns:android=”http://schemas.android.com/apk/res/android"
package="com.example.helloaaos">
4. Modify Android.bp:
The Android Studio project uses gradle as build system and the build configurations are defined in the build.gradle file. The AOSP project uses the Soong build system and the build configurations are defined using blueprint file(.bp).
- Open Android.bp in the AOSP project’s HelloAAOS folder.
- Update the project name, remove unnecessary dependencies, and add required ones from your build.gradle file, ensuring syntax compatibility.
Here is an example of Android.bp file
package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
android_app {
name: "HelloAAOS",
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
libs: ["android.car"],
optimize: {
enabled: false,
},
dex_preopt: {
enabled: false,
},
static_libs: [
"jsr305",
"android.car",
"androidx.appcompat_appcompat",
"com.google.android.material_material",
],
}
5. Add the Project to the Build:
- Navigate to the appropriate file based on your Android version.
- Android 14: packages/services/Car/car_product/build/car_system.mk.
- Android13 or Android 12: packages/services/Car/car_product/build/car.mk.
- Add your new app to the list.
PRODUCT_PACKAGES += \
CarFrameworkPackageStubs \
CarService \
CarShell \
CarDialerApp \
CarRadioApp \
OverviewApp \
CarLauncher \
CarSystemUI \
LocalMediaPlayer \
CarMediaApp \
CarMessengerApp \
CarHTMLViewer \
CarHvacApp \
CarMapsPlaceholder \
CarLatinIME \
CarSettings \
CarUsbHandler \
android.car \
car-frameworks-service \
com.android.car.procfsinspector \
libcar-framework-service-jni \
ScriptExecutor \
HelloAAOS \
6. Build AOSP and Run the Emulator:
- Source the environment using . build/envsetup.sh .
- Choose your target using lunch sdk_car_x86_64-userdebug.
- Build the source code with make -j$(nproc).
- Launch the emulator with emulator & to see your integrated app.
Extending the App: Accessing HVAC Properties
1. Requesting the HVAC Permissions
Add the CONTROL_CAR_CLIMATE permission to AndroidManifest.xml.
<uses-permission android:name=”android.car.permission.CONTROL_CAR_CLIMATE” />
2. Accessing HVAC Data in Your Activity:
Modify your app activity to display data like A/C status. Here’s how to connect to the car system’s services and register for updates:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
mCarApiClient = Car.createCar(this, null, CAR_WAIT_TIMEOUT_WAIT_FOREVER,mServiceConnectionListener);
}
}
private final Car.CarServiceLifecycleListener mServiceConnectionListener = new Car.CarServiceLifecycleListener() {
@Override
public void onLifecycleChanged(Car car, boolean b) {
synchronized (mHvacControllerReady) {
try {
mCarPropertyManager = (CarPropertyManager) car.getCarManager(Car.PROPERTY_SERVICE);
registerHvacPropertyEventListeners();
mHvacControllerReady.notifyAll();
} catch (Exception e) {
Log.e(TAG, "Car not connected in onLifecycleChanged");
}
}
}
};
private void registerHvacPropertyEventListeners() {
for (Integer propertyId : HVAC_PROPERTIES) {
if (mCarPropertyManager.getCarPropertyConfig(propertyId) == null) {
Log.w(TAG, "registerHvacPropertyEventListeners - propertyId: + "
+ VehiclePropertyIds.toString(propertyId) + " is not implemented."
+ " Skipping registering callback.");
continue;
}
mCarPropertyManager.registerCallback(mPropertyEventCallback, propertyId,
CarPropertyManager.SENSOR_RATE_ONCHANGE);
}
}
3. Handling HVAC Property Updates:
The mPropertyEventCallback is triggered whenever an HVAC property changes. Here’s an example of handling the A/C state update:
private final CarPropertyManager.CarPropertyEventCallback mPropertyEventCallback =
new CarPropertyManager.CarPropertyEventCallback() {
@Override
public void onChangeEvent(CarPropertyValue value) {
int areaId = value.getAreaId();
switch (value.getPropertyId()) {
case HVAC_AC_ON:
handleAcStateUpdate(getValue(value));
break;
default:
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Unhandled HVAC event, id: " + value.getPropertyId());
}
}
}
@Override
public void onErrorEvent(int propId, int zone) {
Log.w(TAG, "Could not handle " + propId + " change event in zone " + zone);
}
};
private void handleAcStateUpdate(boolean acState) {
if(Log.isLoggable(TAG, Log.ERROR)) {
Log.e(TAG, "AC State Update Received");
}
// Implement your own logic here
}
Only the HVAC_AC_ON property is handled here. You can extend the application to receive values for other HVAC properties as well.
4. Updating the UI:
Update your UI based on the received AC state (acState) in the handleAcStateUpdate function.
- The property VehiclePropertyIds.HVAC_AC_ON requires platform permissions. Add the following to the Android.bp file
platform_apis: true,
certificate: "platform",
Conclusion
This exercise provided some valuable insights about AAOS and AOSP. Now, it’s your turn to share your learnings in the comments below!
Key takeaways:
- AAOS and AOSP share the same core source code.
- Dedicated directories within AOSP house automotive specific apps, services, and frameworks..
- Apps — packages/apps/car
- Services — packages/services/car
- Frameworks — frameworks/hardware/interfaces/automotive
- HAL and AIDL — hardware/interfaces/automotive/
Stay tuned!
We’ll explore different types of permissions in Android Automotive applications in our next post. We’ll also discuss the process of creating a privileged app and adding it to the AAOS system image.