KINTO Tech Blog
Android Automotive OS

Hello, Android Car App!

Cover Image for Hello, Android Car App!

This article is the entry for day 5 in the KINTO Technologies Advent Calendar 2024 🎅🎄


I'm Onuma, an Android engineer in the Mobile Development Group at KINTO Technologies. I primarily work on developing the My Route, a mobility service app.

In this article, I'll guide you through the process of building Android Automotive OS and developing in-vehicle apps, including both Android Automotive and Android Auto applications.

Running Android Automotive OS on a Raspberry Pi

What is Android Automotive OS?

Android Automotive is an in-vehicle platform built on Android and integrated into the AOSP framework. It supports pre-installed Android apps for IVI systems, as well as second- and third-party Android apps. For more details, refer to the official documentation → https://developer.android.com/training/cars?hl=ja#automotive-os

What is AOSP?

AOSP stands for Android Open Source Project, and all the elements that make up the Android OS are open source and available to the public.

Android Open Source Project

The latest OS developed by Google is released as open source after a certain non-disclosure period. Device developers customize the released OS by adding features or making modifications to suit their specific needs before installing it on smartphones, tablets, and other devices.

What to prepare to build Android Automotive OS

  • PC *It is necessary to meet the hardware requirements mentioned later.
  • Display *A touch monitor is better.
  • RaspberryPi 4B
  • MicroSD
    16GB should be enough.
  • MicroHDMI-HDMI cable

Hardware Requirements for Building

  • OS: Ubuntu 22.04
  • Intel Gold 6226R (16 cores, 32 threads)
  • At least 16 GB of RAM
  • HD: 1TB

*Note: Building on Windows or Mac OS is not supported.

I tried to create an environment on AWS EC2 to build, but I gave up since the Free Tier couldn't meet the required specifications.

Set Up the Build Environment

Install the necessary tools for building.

sudo apt-get install git-core gnupg flex bison build-essential zip curl zlib1g-dev libc6-dev-i386 libncurses5 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig

Add Repo and Local_Manifest

Android OS consists of a large collection of source code.

Repo is used to check out Android source code. The components are loosely coupled, with each one being managed and developed in an independent Git repository. Repo is a tool that manages these numerous Git repositories based on a management file called a Manifest file.

## Install the Repo launcher
repo init -u [https://android.googlesource.​com/platform/manifest](https://android.googlesource.com/platform/manifest) -b android-13.0.0\_r35 --depth=1  

## Add the local_manifest
git clone [https://github.com/grapeup/​aaos_local_manifest.git](https://github.com/grapeup/aaos_local_manifest.git) .repo/local\_manifests

Add dav1d under line 46 <!-- FFmpeg --> of .repo/local_manifests/manifest_brcm_rpi4.xml

Added missing dav1d library in the local manifest by jijith700 · Pull Request #5 · grapeup/aaos_local_manifest · GitHub


## Add missing dav1d library to local_manifest

<!-- FFmpeg -->
  <project path="external/dav1d" name="raspberry-vanilla/android_external_dav1d" remote="github" revision="android-13.0" />

Compile

. build/envsetup.sh
lunch aosp_rpi4-userdebug
make bootimage systemimage vendorimage -j$(nproc)

Flashing and Deploying Images

Clean the MicroSD card.

sudo umount /dev/sdb*
sudo wipefs -a /dev/sdb*
sudo wipefs -a /dev/sdb

Then create four partition tables and flash the images to the MicroSD card.

There are three images to flash onto the microSD card: boot.img, system.img, and vendor.img.

As I thought I could flash the images using the command sudo dd if=boot.img of=/dev/sdb1 bs=1M, I gave it a try, but the steps were too complicated. So, I used a partition editing tool called GParted.

GParted

Boot Android Automotive OS

Insert the microSD card into the Raspberry Pi to boot. It's convenient if you connect a touch monitor to the Raspberry Pi, you can operate it without a mouse. But I don't have a touch monitor, so I'm connecting a PC monitor instead ;-; Automotive video

Develop In-Vehicle Apps that Run on Android Auto and Android Automotive OS

Next, I'll walk you through the basics of implementing and debugging in-vehicle apps on Android. While Android Auto works by connecting your smartphone to display apps on the car's screen, Android Automotive OS has Android built directly into the vehicle system, allowing apps to be installed directly.

This time, I implemented a navigation app as a trial. The following development environment is a Mac.

Supported app categories and corresponding Android APIs

Category Description Corresponding Android API
Media Apps for music, podcasts and audiobooks Use the MediaBrowserService to browse content and control playback. Use the MediaSession to notify the system of playback status and metadata.
Navigation Turn-by-turn navigation with audio and visual guidance Use the NavigationManager from the CarAppLibrary to control navigation start, end, destination setting, and turn-by-turn guidance.
Point of Interest (POI) Apps to find locations such as parking lots, EV charging spots, gas stations, etc. Use the PlaceClient to implement features such as finding locations, getting more information, and Place Autocomplete. Use the PlaceListMapTemplate from the CarAppLibrary to view POIs on a map.
Messaging (Android Auto only) Hands-free message replies with voice input Use the MessagingManager from the CarAppLibrary to control sending and receiving messages, voice input, and sending template messages.
Game Apps for entertainment while parked Use the ScreenManager from the CarAppLibrary to view the game screen while parked. Use the InputManager to receive control inputs for your game.
Browser & Video Browser integration and video playback features (specific to AAOS, often used while parked) Use the WebTemplate from the CarAppLibrary to display web content. Use the VideoTemplate to play video content. These templates are recommended for use only while parked.

Supplement

  • I've summarized the key points from the table in the Official Documentation.
  • Since new categories are added every year, even if you can't widely release your app yet, there is a possibility it could be released in the future.
  • CarAppLibrary is a Jetpack library for Android Auto and Android Automotive OS app development.
  • PlaceClient is a client that uses the Google Places API.

Desktop Head Unit (DHU)

  • What is a DHU? DHU is a tool for emulating the Android Auto environment on a desktop. It allows you to simulate the in-vehicle experience without using an actual in-vehicle device.

  • Why use DHU? You can test how the app operates and displays in an in-vehicle environment. It lets you debug and verify that your UI/UX complies with guidelines to avoid distracting drivers.

Run the DHU

The following are required to run the DHU:

  • MacBook
  • Android device
  1. Install the Android Auto Desktop Head Unit Emulator in the SDK Manager.
  2. Make sure there is a desktop-head-unit in Library/Android/sdk/extras/google/auto.
  3. Grant permissions to the desktop-head-unit.
chmod +x ./desktop-head-unit
  1. Forward the socket connection to the same port number on the Android device.
adb forward tcp:5277 tcp:5277
  1. Open the Auto settings on your Android device. Tap [See all apps] > [Android Auto] > [Advanced] > [Additional settings in the app]. Tap the version and permission information about 10 times to enable developer mode.

android_auto_developer_mode

  1. Run the DHU.
./desktop-head-unit --usb

android_auto_emulator

About Host

When running an app created for Android Auto or Android Automotive in a compatible car, the app doesn't interact directly with the car. At this stage, the connection destination is the Android Auto app on the Android device. During the DHU installation process, connecting to the actual device via USB is necessary since it needs to communicate with the Android Auto app, which serves as the host.

The Android Auto app is referred to as the host, and all Auto-compatible apps interact with this host. In the case of an Android Automotive-compatible car, the OS is built into the vehicle system itself, so Android Automotive acts as the host.

android_automotive_host

Libraries

CarAppLibrary is a Jetpack library for Android Auto and Android Automotive OS app development.

Apps built using the CarAppLibrary run through the host app rather than running directly on Auto or Automotive.

How it works

Declare the version of CarAppLibrary in the project-level build.gradle.

buildscript {
    ext {
        car_app_library_version = '1.4.0'
    }
}
dependencies {
  ...
  implementation "androidx.car.app:app:$car_app_library_version"
  ...
}

Add services and sessions

Add a class that inherits from CarAppService. You need to extend the CarAppService bound by the host.

In the intent filter, you need to declare androidx.car.app.category.POI as the category for the car app.

        <service
            android:name="com.example.places.carappservice.PlacesCarAppService"
            android:exported="true">
            <intent-filter>
                <action android:name="androidx.car.app.CarAppService" />
                <category android:name="androidx.car.app.category.POI" />
            </intent-filter>
        </service>

The CarAppService abstract class cannot be overridden, such as onBind or onUnbind. The library takes care of proper interaction with the host app for you. You only need to implement createHostValidator and onCreateSession.

The HostValidator returned by createHostValidator is referenced when the CarAppService is bound to verify that the host is trusted, and that the bind fails if it does not match the parameters defined by the host. ALLOW_ALL_HOSTS_VALIDATOR is a HostValidator that can only be used for validation.

class PlacesCarAppService : CarAppService() {

    override fun createHostValidator(): HostValidator {
        return HostValidator.ALLOW_ALL_HOSTS_VALIDATOR
    }

    override fun onCreateSession(): Session {
        return PlacesSession()
    }
}

Add the PlacesSession class.

class PlacesSession : Session() {
    override fun onCreateScreen(intent: Intent): Screen {
        return MainScreen(carContext)
    }
}

Template

You need to choose from predefined templates and implement them according to the guidelines. The UI and UX of car apps are restricted because the UI needs to be optimal for drivers.

template Reference: Official template documentation

In addition, add the necessary permissions to access templates for displaying maps.

<uses-permission android:name="androidx.car.app.MAP_TEMPLATES" />

List Location Information

Once launched, locations will be listed.

The UI can be implemented using Composable.

Add a MainScreen that inherits Screen from CarAppLibrary. To display the list of locations and a map, return a PlaceListMapTemplate using onGetTemplate. Templates are implemented using the Builder Design Pattern. Pass the items to be listed using setItemList and build the template to return.

Use ItemListBuilder to build the items to be listed.

class MainScreen(
    carContext: CarContext,
) : Screen(carContext) {
    override fun onGetTemplate(): Template {
        val placesRepository = PlacesRepository()
        val itemListBuilder =
            ItemList.Builder()
                .setNoItemsMessage("No data")

        placesRepository.getPlaces()
            .forEach {
                itemListBuilder.addItem(
                    Row.Builder()
                        .setTitle(it.name)
                        // Each item in the list must have a DistanceSpan added to the title or text line.
                        .addText(
                            SpannableString(" ").apply {
                                setSpan(
                                    DistanceSpan.create(
                                        Distance.create(Math.random() * 100, Distance.UNIT_KILOMETERS),
                                    ),
                                    0,
                                    1,
                                    Spannable.SPAN_INCLUSIVE_INCLUSIVE,
                                )
                            },
                        )
                        .setOnClickListener {
                            screenManager.push(DetailScreen(carContext = carContext, placeId = it.id))
                        }
                        .setMetadata(
                            Metadata.Builder()
                                .setPlace(
                                    Place.Builder(CarLocation.create(it.latitude, it.longitude))
                                        .setMarker(PlaceMarker.Builder().build())
                                        .build(),
                                )
                                .build(),
                        ).build(),
                )
            }

        return PlaceListMapTemplate.Builder()
            .setTitle("Places")
            .setItemList(itemListBuilder.build())
            .build()
    }
}

View detailed location information

Use PaneTemplate to implement the detail screen.

class DetailScreen(carContext: CarContext, private val placeId: Int) : Screen(carContext) {
    private var isFavorite = false

    override fun onGetTemplate(): Template {
        val place = PlacesRepository().getPlace(placeId)
            ?: return MessageTemplate.Builder("Place not found")
                .setHeaderAction(Action.BACK)
                .build()

        val navigateAction = Action.Builder()
            .setTitle("Navigate")
            .setIcon(
                CarIcon.Builder(
                    IconCompat.createWithResource(
                        carContext,
                        R.drawable.baseline_navigation_24
                    )
                ).build()
            )
            .setOnClickListener {  carContext.startCarApp(place.toIntent(CarContext.ACTION_NAVIGATE)) }
            .build()

        val actionStrip = ActionStrip.Builder()
            .addAction(
                Action.Builder()
                    .setIcon(
                        CarIcon.Builder(
                            IconCompat.createWithResource(
                                carContext,
                                R.drawable.baseline_favorite_24
                            )
                        ).setTint(
                            if (isFavorite) CarColor.RED else CarColor.createCustom(
                                Color.LTGRAY,
                                Color.DKGRAY
                            )
                        ).build()
                    )
                    .setOnClickListener {
                        isFavorite = !isFavorite
                        // To capture updates to the screen state, call invalidate() to call `onGetTemplate` again.
                        invalidate()
                    }.build()
            )
            .build()

        return PaneTemplate.Builder(
            Pane.Builder()
                .addAction(navigateAction)
                .addRow(
                    Row.Builder()
                        .setTitle("Coordinates")
                        .addText("${place.latitude}, ${place.longitude}")
                        .build()
                ).addRow(
                    Row.Builder()
                        .setTitle("Description")
                        .addText(place.description)
                        .build()
                ).build()
        )
            .setTitle(place.name)
            .setHeaderAction(Action.BACK)
            .setActionStrip(actionStrip)
            .build()
    }
}

Launch the app

location

Possible Errors When Trying to Launch Other Apps

Caused by: androidx.car.app.HostException: Remote startCarApp call failed

An error may occur when attempting to start navigation (where startCarApp is called). This is likely because a navigation app is not installed. You can easily find a navigation app by searching in the Play Store on the emulator.

Vehicle Properties Available in the App

Although not yet verified, the following properties should be available. The setting values may be adjustable in the emulator. Reference

  1. Vehicle Speed
    The current speed of the vehicle can be obtained. It is typically provided in km/h and is used for actions based on speed limits and driver assistance features.
  2. Fuel Level
    For gasoline vehicles, you can obtain the remaining fuel level in the tank. This can be used for features like "low fuel" warnings or suggestions for the nearest fuel station.
  3. Battery Level
    For EVs and Hybrids, you can monitor the state of the vehicle's battery. It is used to display charging status or remaining battery levels.
  4. Door Status
    The open/closed status of each door (front, rear, trunk, and hood) can be obtained. You can set up notifications when a door is left open or alerts to prevent forgetting to close it.
  5. Light Status
    The on/off status of the vehicle lights (headlights, high beams, fog lights, etc.) can be obtained. This allows for night mode switching and providing feedback to the driver.
  6. Engine Status
    The on/off/idling status of the engine can be obtained. The application can restrict certain actions when the engine is off.
  7. Parking Brake Status
    The status of whether the parking brake is applied or released can be obtained. This can be used to control app functionality and interactions while parked.
  8. Gear Position
    The position of the shift lever (Park, Reverse, Neutral, Drive, etc.) can be obtained. This allows for automatic activation of the back camera and interface changes based on the gear selection.
  9. Tire Pressure
    The tire information such as tire pressure can be obtained. This allows notification of low pressure warnings and maintenance alerts.
  10. External Temperature
    The external temperature can be obtained, allowing it to be used for weather-based interfaces or driver notifications based on driving conditions.
  11. Seat Occupancy Status
    The presence of passengers in each seat and seatbelt usage can be obtained. This is used to display warnings when seat belts are not fastened for safety reasons.
  12. Window Status
    The open/closed state of each window can be obtained. For example, a notification can be sent if a window is left open when the vehicle is turned off.
  13. HVAC Status
    The settings and status of the vehicle's air-conditioning system (heating, cooling, fan speed, and airflow direction) can be obtained. This allows the app to manage a comfortable in-car environment.
  14. GPS Location
    The vehicle's current GPS location can be obtained. This enables navigation apps and location-based services.
  15. Wiper Status
    The operational state of the wipers can be obtained. This helps adjust the UI based on weather and visibility conditions.

Conclusion

Thank you for reading to the end.

Android Automotive OS

The quality of the Android open source is well-maintained, making it easy enough for a beginner to clone, build, and boot it. However, the required PC specs are quite high. One of our engineers pointed out that boards with Android Automotive OS pre-installed are available worldwide, and I couldn't help but think, "Why didn’t you tell me sooner?" Nonetheless, getting the OS up and running was a highly rewarding experience.

Auto and Automotive App Development

This article ended up providing a broad overview of in-vehicle app development, but we discovered that the implementation process involves just a few steps.

That said, the concepts of host app and emulator setup can be somewhat challenging to grasp.

Since automotive app development doesn't allow for much UI customization, the real challenge and fun lie in refining what the app can do. In the future, as autonomous driving becomes mainstream, more categories may emerge, allowing drivers to enjoy gaming and other experiences.

Bonus

Reflecting on my childhood memories of driving, I was inspired by Hyuga-san's article to create AI-generated music. Here's what I came up with. It turned out pretty good and atmospheric. I suppose only my colleagues would stick around this long. Looking forward to hearing your thoughts! https://soundcloud.com/numami-775711983/5qclozsqk1mz

Facebook

関連記事 | Related Posts

We are hiring!

【KINTO FACTORYバックエンドエンジニア】KINTO FACTORY開発G/大阪

KINTO FACTORYについて自動車のソフトウェア、ハードウェア両面でのアップグレードを行う新サービスです。トヨタ・レクサスの車をお持ちのお客様にOTAやハードウェアアップデートを通してリフォーム、アップグレード、パーソナライズなどを提供し購入後にも進化続ける自動車を提供するモビリティ業界における先端のサービスの開発となります。

【PdM】/KINTO FACTORY開発G/東京・大阪

KINTO FACTORYについて自動車のソフトウェア、ハードウェア両面でのアップグレードを行う新サービスです。トヨタ・レクサスの車をお持ちのお客様にOTAやハードウェアアップデートを通してリフォーム、アップグレード、パーソナライズなどを提供し購入後にも進化続ける自動車を提供するモビリティ業界における先端のサービスの開発となります。

イベント情報

P3NFEST Bug Bounty 2025 Winter 【KINTOテクノロジーズ協賛】