In this codelab, we will learn how to create and deploy a new project for Android Automotive OS for both the emulator and the reference hardware platform that is available at the Codelab space during Google I/O 2019.

To achieve this, we will use Android Studio's new project template, customize the resulting app with our own assets, and deploy the app locally in an Android for Automotive environment.

What we'll learn

Hardware we'll need

Software we'll need

Android Automotive OS is an Android-based infotainment system that is built in to vehicles (with the first Android-powered vehicles launching in 2020). The car's system is a stand-alone Android device that is optimized for vehicles.

Instead of using your phone app, users install a driver-optimized version of your app directly onto the device. You can reuse services that you create for Android Automotive OS for Android Auto, but there are some activities such as user authentication and settings that you must design for vehicle user interfaces.

For more details, check out the developer documentation.

Make sure that the Android Studio flags to enable Automotive OS templates and emulator images are correctly set. To turn on the automotive features, create or modify `studioFlags.xml` file at the following location:

Then make sure that the `npw.templates.automotive` key is set to true:

<application>
  <component name="StudioFlags">
    <option name="data">
      <map>
        <!-- This flag enables experimental automotive templates -->
        <entry key="npw.templates.automotive" value="true" />
      </map>
    </option>
  </component>
</application>

Once the Automotive features are turned on in Android Studio, from the welcome screen, select Configure > AVD Manager.

Or, if we already have a project open, we can achieve the same by selecting Tools > AVD Manager.

Then, select "Create Virtual Device" and choose one of the options under Automotive category.

For the system image, we can select Pie (or a newer version if available, as long as the target mentions "Automotive").

In the next screen, we can give our emulator a name if we want. The rest of the configuration can be left as the default values. Once we select Finish, the emulator will be ready to launch by selecting the Play button under Actions.

If everything went well, we should see the Android Automotive OS emulator which looks similar to this:

Starting in Android Studio 3.5, a new set of templates for Android for Automotive are available. Starting a new project for this new platform is as simple as selecting Start a new Android Studio project from Android Studio welcome screen, then selecting one of the templates under Automotive.

For this codelab, we will be using the Media service template under the Automotive tab.

After selecting Next, we will be presented with a new project that includes everything we need to create a music player app for Automotive OS. Give it any name you'd like (we chose Road Media Player), select Kotlin as the language and make sure that Use AndroidX artifacts is checked. Since Android Automotive OS is only available starting with Android Pie, that must be the minimum API level.

Most likely, we are building an app for multiple platforms and not just for Android for Automotive. There are different strategies that we can follow, and the most optimal one will depend on a number of factors, including amount of shared code, and size or preferences of our engineering team.

Ideally, we want to reuse as much code as possible across Android platforms. One potential approach to achieve that is to have a single top-level project and several modules -- some modules act as libraries that are shared across platforms, and others will be specific to one of the platforms. With this approach, we can build a single artifact which is an Android App Bundle (.aab) that can be uploaded to the Play Store. The main benefit of this approach is that we can leverage the Play Store infrastructure so users receive only the portion of the bundle relevant to their device through Dynamic Delivery of modules. Assets and modules will be stripped automatically so users only download the bits necessary for their specific device configuration, saving bandwidth and device storage space.

To read more about app bundles, take a look at the documentation.

Once the project has been created, Gradle may take a couple of minutes to sync. Once that is done, the Automotive OS project created with the template will contain 3 modules: automotive, mobile and shared.

automotive

This module contains the code and assets that are included into the application that gets deployed Android Automobile OS only. If we look closely, the only code that we'll find are the test and androidTest placeholders. Of course, there are some assets too but, most importantly, let's take a closer look at the AndroidManifest file:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.example.roadmediaplayer">

    <uses-feature
        android:name="android.hardware.type.automotive"
        android:required="true" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" />

</manifest>

To declare an app as compatible with Automotive OS, we must set the android.hardware.type.automotive feature to true. It is also worth noting that we omitted the Activity tag.

Activities work a little differently in Automotive OS compared to normal Android applications, and developers should expect to maintain separate versions of their layouts and activities for mobile and automotive. In core use-cases, such as media playback, the UI is even drawn by the OS and not by our fragment / activity -- we just need to setup our media session and browser service correctly. Other activities will typically be launched by the OS using an explicit intent.

If we had any activities declared in the manifest for automotive (or shared), we have to make sure that the launcher and main intent filters are not set. To illustrate that with an example, here is what a normal activity declaration would look like on mobile, inside of the Application tag:

<activity android:name=".MyActivity">
    <intent-filter>
        <!-- These need to be deleted for Android Automotive OS -->
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

For Android Automotive OS, we need to make sure that action.MAIN and category.LAUNCHER are not present:

<activity android:name=".MyActivity">
    <intent-filter>
    <!--
        We can still have other intent filters here, just not action.MAIN
        or category.LAUNCHER. Apps normally will want to implement
        action.ACTION_LOGIN and action.APPLICATION_PREFERENCES activities.
    -->
    </intent-filter>
</activity>

mobile

The mobile module contains code and assets that will be deployed to mobile and tablet devices. This is the normal Android application, and has a very standard-looking manifest file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.example.roadmediaplayer">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

shared

The shared module contains code that will be deployed to both other modules. This is done via gradle dependencies, since both automotive and mobile contain implementation project(':shared') in the dependencies section of their respective build.gradle files. Here's what the manifest file looks like for the shared module:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.example.roadmediaplayer">

    <application>

        <meta-data
            android:name="com.google.android.gms.car.application"
            android:resource="@xml/automotive_app_desc" />


        <!-- Main music service, provides media browsing and media playback services to
         consumers through MediaBrowserService and MediaSession. Consumers connect to it through
         MediaBrowser (for browsing) and MediaController (for playback control) -->
        <service
            android:name="com.android.example.roadmediaplayer.MyMusicService"
            android:exported="true">
            <intent-filter>
                <action android:name="android.media.browse.MediaBrowserService" />
            </intent-filter>
        </service>

    </application>

</manifest>

Once again, there is no Activity tag. There is a declaration for the music service provided by this app, which we won't go into detail in this codelab. If you want to learn more about that, check out the relevant Android music player codelab.

Another interesting thing in the manifest is the following snippet inside of the Application tag:

<meta-data
    android:name="com.google.android.gms.car.application"
    android:resource="@xml/automotive_app_desc" />

Inside of the referenced XML file, res/xml/automotive_app_desc, we find the following:

<?xml version="1.0" encoding="utf-8"?>
<automotiveApp>
    <uses name="media" />
</automotiveApp>

This indicates that our app is a media app and, as such, it will appear under the media center of the Android Automotive OS UI:

As an Android platform, Google Play Services is also available in Automotive OS. The maven coordinates for the SDKs for Google Play Services will remain same in both mobile development and Automotive OS development. However, there are a few differences compared to mobile Google Play Services.

UX/UI elements

UX/UI components of Google Play Services are optimized for cars. For example, this is what the authentication flow looks like in mobile:

But in Android Automotive OS, it looks like this:

Even though we should still take the different form factor into consideration when designing our app's user flows, we can trust that Google Play Services is optimized for Android Automotive OS.

Limitations

Some Google Play Services components have been stripped down to reduce the footprint in the platform; for example, Google Cast is not available. Available API calls can be found on the developer website. For code that is completely separate for Automobile OS and mobile, then we can just omit those components. If we have shared code (like in the template's shared module), we have to make sure that the relevant API is only called in non-Automotive devices:

import com.google.android.gms.auth.api.Auth
import com.google.android.gms.cast.Cast
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.common.api.AvailabilityException
import com.google.android.gms.task.Task

class MySharedModule {
   // ...
   fun someFunctionThatRequiresApi() {
       val task = GoogleApiAvailability.getInstance()
           .checkApiAvailability(
               Auth.getInstance(context),
               Cast.getInstance(context)
           )
           .continueWith({ t ->
                   val exception = t.getException()
                   if (exception is AvailabilityException) {
                       LOG.w("Cast API or Auth API not available", exception)
                       // Code for when API is not available
                       // ...
                   } else if (exception != null) {
                       // An exception not related to API availability is thrown
                       throw exception
                   } else {
                       // ...
                       // Code for when API is available
                   }
               })
   }
   // ...
}

To make the app ours, we will download this image and create a new icon derived from it:

To modify the app's icon, right click on automotive > res and select new > Image Asset:

Make sure that the icon type is Launcher Icons (Adaptive and Legacy) and that the name is ic_launcher. Then, select the desired image as the foreground image and #FCE900 as the background color.

Once we are happy with the look, select Next and then Finish.

Now we are ready to test local app deployment; but when we select automotive in Android Studio's launch configuration, we may notice a warning.

Even worse, if we try to launch it, we will see the following error:

Error running 'automotive': Default Activity not found

This is because, as explained earlier, users don't directly launch our application so we can't set a default (i.e. main) Activity. Instead, it is the Android Automotive OS that launches and interacts with our application via explicit intents and interacting with our media session or media browsing service. This allows, for example, for the OS to jump directly into our app's media playback without having to launch an unnecessary Activity first.

To get rid of the error, we should change the configuration to only install but not launch our application. Under Launch options, select Nothing for the Launch target:

Now, when we click the Run button, our app will be installed on the selected device.

After the app has been successfully installed, it will show up in two different locations on the device. First, under the All apps section:

Second, since we declared our app as a media app, it will also appear in the media center:

Launching the app, however, will produce some extremely disappointing results with a barebones UI claiming that "This folder is empty":

That's because we have not implemented any actual functionality into our app yet! But that's out of the scope for this codelab. To learn more details about the media playback implementation best practices, take a look at the official music player sample for Android (UAMP), which should serve as the reference implementation for media apps in Android Automotive OS.

You've finished the codelab and successfully! To recap, these are the things that we covered:

We recommend that you take a look at the official music player sample for Android (UAMP) as a starting point for your next media app, or if you want to learn more about media apps on Android Automotive OS. As a follow-up to this codelab, you can clone the GitHub repo locally and install the app on the emulator or the reference hardware device.