Android Auto を学ぶその1 とにかく表示してみる

Android Auto を学びたいな〜と思って勉強してみることにしました。

概要

Android で自動車にてアプリを動かす仕組みは2つあります。一つは Android Auto、もう一つが Android Automotive。

Android Auto はスマートフォンで動くアプリを車のナビ画面に投影するイメージ。ナビ側が Android Auto をサポートしている必要があります。動作にはスマートフォンが必要で、純粋な車用アプリというよりも、スマフォ用アプリに車用画面を用意してあげる、という形になります。

Android Automotive は、車のナビに Android OS が組み込まれている場合に利用できます。スマートフォンを必要とせず、車の画面のみでアプリをインストールできます。この機能を積んでいる車は2022/09/26現在市場にまだ全然ないはずで、おそらくボルボとポルシェの一部にしか存在していないと思います。日本車に至っては1台も存在しない気がします。

自分の持っている車(Mazda3)は Android Automotive を使うことができないので、Android Auto にまとを絞って学んでいきます。なお、Android Auto が前提のアプリは避けるべきであるため、基本的には車に接続しなくとも単体で動くアプリ + 車用画面があるアプリとするべきですが、ここではとりあえず Android Auto の画面だけに絞っています。

ホストについて

Android Auto 対応車で作成したアプリを動かすとき、アプリは車と直接やりとりする訳ではありません。このときの接続先はスマフォに入っている Android Auto アプリです。Android Auto アプリはホストと呼ばれ、全ての Auto 対応アプリはこのホストとやりとりします。もし Android Automotive 対応の車で動かす場合は車載器に OS が入っているので、Android Automotive がホストになります。

developer.android.com このドキュメントに、ホストとは何かが記載されています。

ホストは、ライブラリの API によって提供される、アプリを車で実行するための機能を実装するバックエンド コンポーネントです。ホストは、アプリの検出とそのライフサイクルの管理、モデルのビューへの変換、ユーザー操作のアプリへの通知にいたるまで、幅広い役割を担います。モバイル デバイスでは、このホストは Android Auto によって実装されます。

API Level について

Android Auto には API Level という概念が存在しています。これはどの機能までを利用可能かを示すものです。ホストのバージョンに左右されます。現在利用可能なのは API Level 5です。これを動かすには Android Auto 8.1 以降のアプリが端末にインストールされている必要があります。

個人的に重要と考えているのは API Level 3 で追加された API です。これがあると CarHardwareManager を利用できます。CarHardwareManager を使うことができれば、車のコンピュータから吐き出される速度、燃料残量、ジャイロ、位置情報、ETCカードの状態などを取得することが可能になるのです。絶対取得したいですよね!!それぞれの API Level で何ができるかはリリースノートを参照することでわかります。

developer.android.com

とりあえず文字列を表示してみる

まずは簡単な表示を、簡単なコードで試してみるのが良さそうです。早速コードを書いてみることにします。まずは何を書く必要があるのかをみてみましょう。

  • gradle に Android Auto の依存を追加する
  • AndroidManifest.xmlAndroid Auto に必要な記述を追加する
  • automotiveApp を記載した xml リソースを作成する
  • androidx.car.app.CarAppService クラスを実装する
  • androidx.car.app.CarAppService から起動される androidx.car.app.Session を実装する
  • androidx.car.app.Session から呼ばれる androidx.car.app.Screen を実装する

gradle に Android Auto の依存を追加する

app/build.gradle に、以下の依存を追加します。

dependencies {
    implementation "androidx.car.app:app:1.3.0-beta01"
    implementation "androidx.car.app:app-projected:1.3.0-beta01"
}

2022/11/07時点の最新版は 1.3.0-beta01 であり、これを試すのであれば API Level 5 の機能が利用可能になります。端末に最新の Android Auto アプリをインストールしておきましょう(8.1 以降でないと API 呼び出しでクラッシュします)。 余談ですが1.3.0系がでた当初、Android Auto は 8.1 より前のバージョンしかストアにありませんでした。そのため 1.3.0 を利用するには Android Auto アプリのテスターになってより新しいバージョンを得る必要があったのかなと思います。ですがテスター募集はもう締め切られているので、SDK はあるが動かせない状態になっていました。

AndroidManifest.xmlAndroid Auto に必要な記述を追加する

まずは既存の application 内にエントリポイントとなる service クラスの記述を追加しましょう。

<service
            android:name=".SampleAndroidAutoAppService"
            android:exported="true">
            <intent-filter>
                <action android:name="androidx.car.app.CarAppService"/>
            </intent-filter>
        </service>

次に、最低限必要な CarApiLevel 、及びテンプレートを利用する旨を宣言しましょう。automotive_app_desc ファイルは次で作成します。

<application>
...
        <meta-data
            android:name="androidx.car.app.minCarApiLevel"
            android:value="1"
            tools:ignore="MetadataTagInsideApplicationTag" />
        <meta-data
            android:name="com.google.android.gms.car.application"
            android:resource="@xml/automotive_app_desc"/>
</application>

automotiveApp を記載した xml リソースを作成する

automotive_app_desc.xml を作成しましょう。今回のアプリでは、情報を表示するためにテンプレートと言うものを使うことになります。この時、<automotiveApp> 内に利用する旨を宣言する必要があります。

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

もしsmsを利用するアプリを作りたい場合、ここに <uses name="sms" /> を、またメディアアプリを作りたい場合、<uses name="media"/> を宣言することになります。

androidx.car.app.CarAppService クラスを実装する

Android Auto では、Activity ではなく Service が起動されます。そして車向けアプリ用として Service を拡張した CarAppService が用意されているので、これを継承する形になります。

developer.android.com

class SampleAndroidAutoAppService : CarAppService() {
    override fun createHostValidator(): HostValidator {
        return if (applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE != 0) {
            HostValidator.ALLOW_ALL_HOSTS_VALIDATOR
        } else {
            HostValidator.Builder(applicationContext)
                .addAllowedHosts(R.array.hosts_allowlist_sample)
                .build()
        }
    }

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

createHostValidator は、接続しているホストをチェックするものです。何をチェックしたいのでしょうか?今回の場合、Android Auto アプリからの呼び出しかどうかをチェックしているのです。

今回のコードの場合、デバッグ中は HostValidator.ALLOW_ALL_HOSTS_VALIDATOR を返すことになります。この実態は空っぽの許可リストを持ち、全てを許可するフラグを ON にした HostValidator クラスのインスタンスです。

@NonNull
    public static final HostValidator ALLOW_ALL_HOSTS_VALIDATOR = new HostValidator(null,
            new HashMap<>(), true);

そうでない場合は else に入ることになります。ここでは、R.array.hosts_allowist_sample をビルダに渡していますが、この中身はどうなっているのでしょう?

    <string-array name="hosts_allowlist_sample" translatable="false">
        

        
        <item>fdb00c43dbde8b51cb312aa81d3b5fa17713adb94b28f598d77f8eb89daceedf,
            com.google.android.projection.gearhead</item>
        
        <item>70811a3eacfd2e83e18da9bfede52df16ce91f2e69a44d21f18ab66991130771,
            com.google.android.projection.gearhead</item>
        
        <item>1975b2f17177bc89a5dff31f9e64a6cae281a53dc1d1d59b1d147fe1c82afa00,
            com.google.android.projection.gearhead</item>

        
        <item>c241ffbc8e287c4e9a4ad19632ba1b1351ad361d5177b7d7b29859bd2b7fc631,
            com.google.android.apps.automotive.templates.host</item>
        
        <item>dd66deaf312d8daec7adbe85a218ecc8c64f3b152f9b5998d5b29300c2623f61,
            com.google.android.apps.automotive.templates.host</item>
        
        <item>50e603d333c6049a37bd751375d08f3bd0abebd33facd30bd17b64b89658b421,
            com.google.android.apps.automotive.templates.host</item>
    </string-array>

com.google.android.projection.gearhead とはなんでしょうか?その答えは Android Auto アプリのパッケージ名です。 https://play.google.com/store/apps/details?id=com.google.android.projection.gearhead&hl=ja&gl=US

試しに、DHU に接続すると com.google.android.projection.gearhead パッケージ名を検証している様子を窺い知ることができます。

androidx.car.app.CarAppService から起動される androidx.car.app.Session を実装する

Session を実装しましょう。Session は onCreateScreen に abstract メソッドがあるのですが、これを上書きして Screen を実装します。この Screen で、カーナビ画面に表示する画面を実装しますが、ここでは Screen を実装するクラスを別に分けることにします。

class SampleAndroidAutoAppSession : Session() {
    override fun onCreateScreen(intent: Intent): Screen {
        return SampleAndroidAutoAppScreen(carContext)
    }
}

Screen を実装するには carContext が必要です。これは Session がプロパティとして持っているのでそのまま渡せば良いです。

androidx.car.app.Session から呼ばれる androidx.car.app.Screen を実装する

クルマの画面に出す UI を実装しましょう!Android Auto では、自由に UI を作成するのは許されていません(とある技を使えばなんでも表示できますが、規約的に許されていません)。Template から選んで表示することになります。 developer.android.com

また、表示内容の更新回数にも厳しい制限があることに注意が必要です。Template によって異なりますが、今回書くコードの場合、6回表示内容を更新したらクラッシュします。 developer.android.com

class SampleAndroidAutoAppScreen(carContext: CarContext) : Screen(carContext) {
    override fun onGetTemplate(): Template {
        val carAppLevel = carContext.carAppApiLevel
        val row = Row.Builder().setTitle("carAppLevel: $carAppLevel").build()
        val pane = Pane.Builder().addRow(row).build()
        return PaneTemplate.Builder(pane)
            .setHeaderAction(Action.APP_ICON)
            .build()
    }
}

ここまでやれば、とりあえず実行して画面に表示できるはずです!次回は DHU(Desktop Head Unit) を使い、コンピュータの画面上で Android Auto をテストしてみましょう。