0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Android Automotive OS (AAOS) の「走行中」を adb で再現する方法

0
Posted at

はじめに

「車両が走り出した瞬間にアプリの画面が切り替わる」 そんな AAOS アプリはどのように作成されているのか?

AAOS Emulator では adb から VHAL イベントを注入できるため、実機がなくても車速やギアなどの車両状態を自由に変更してアプリの挙動を確認できる。

今回は、車両状態に応じて「走っています」「止まっています」を切り替えるシンプルなアプリを題材に、実装方法と検証結果を紹介する。

検証を始める前は「車速を変更すれば走行状態になるだろう」と考えていた。しかし実際に試してみると、表示を切り替えていたのは速度ではなくギアだった。その理由も含めて見ていく。

作成したもの

画面中央の文字だけを車両状態に応じて切り替えるだけのアプリを作成した。

  • UX_RESTRICTIONS_NO_VIDEO が有効 → 走っています
  • 無効 → 止まっています
停止中 走行中
stopped.png moving.png

Emulator で試す

AAOS Emulator では inject-vhal-event を使って VHAL の値を書き換えられる。

まずはギアを変更してみる。

# GEAR_SELECTION
adb shell dumpsys activity service com.android.car inject-vhal-event 0x11400400 8
adb shell dumpsys activity service com.android.car inject-vhal-event 0x11400400 4

ログを見ると UX_RESTRICTIONS_NO_VIDEO が切り替わり、画面表示も期待どおり変化した。

D VehicleStateDemo: activeRestrictions=0xff, moving=true
D VehicleStateDemo: activeRestrictions=0x0,  moving=false

ここまでは予想どおりだった。

速度を変えても画面は切り替わらない

次に、速度だけを変更してみる。

# PERF_VEHICLE_SPEED
adb shell dumpsys activity service com.android.car inject-vhal-event 0x11600207 10
adb shell dumpsys activity service com.android.car inject-vhal-event 0x11600207 0

「車速が 10 になれば『走っています』へ切り替わる」と思っていたが、画面は変わらなかった。

ログを見ても UX_RESTRICTIONS_NO_VIDEO は変化していない。

なぜ速度ではなくギアで切り替わるのか

理由は、CarUxRestrictionsManager が速度を直接見ているわけではないためだ。

AAOS は速度だけではなく、ギアやパーキングブレーキなどの情報から Driving State を計算し、その結果をもとに UX Restriction を決定している。

既定の設定では

  • PARKED
  • IDLING
  • MOVING

のうち、IDLINGMOVING に同じ UX Restriction が割り当てられている。

そのため Park を抜けた時点で UX_RESTRICTIONS_NO_VIDEO が有効になり、その後は速度が変わっても状態は変化しない。

このため、速度を直接判定するより CarUxRestrictionsManager が提供する UX Restriction を利用した方が AAOS の判定と一致する。

実装

実装は CarUxRestrictionsManager を監視するだけでよい。

Car.createCar() で Car API に接続し、CAR_UX_RESTRICTION_SERVICE を取得する。画面表示中だけ Listener を登録し、終了時に解除する。初回表示ではコールバックが来ないため、currentCarUxRestrictions を一度反映している。

class MainActivity : ComponentActivity() {
    private var car: Car? = null
    private var uxManager: CarUxRestrictionsManager? = null
    private var moving by mutableStateOf(false)
    private val listener = CarUxRestrictionsManager.OnUxRestrictionsChangedListener { r ->
        applyRestrictions(r)
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        car = Car.createCar(this)
        uxManager = car?.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE) as? CarUxRestrictionsManager
        setContent {
            WebViewOnAndroid14Theme {
                Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
                    Text(
                        text = if (moving) "走っています" else "止まっています",
                        style = MaterialTheme.typography.displayLarge
                    )
                }
            }
        }
    }
    override fun onStart() {
        super.onStart()
        val m = uxManager ?: return
        m.registerListener(listener)
        applyRestrictions(m.currentCarUxRestrictions) // 初期表示を反映
    }
    override fun onStop() {
        super.onStop()
        uxManager?.unregisterListener()
    }
    override fun onDestroy() {
        super.onDestroy()
        car?.disconnect()
        car = null
    }
    private fun applyRestrictions(r: CarUxRestrictions?) {
        val active = r?.activeRestrictions ?: 0
        moving = active and CarUxRestrictions.UX_RESTRICTIONS_NO_VIDEO != 0
        Log.d(TAG, "activeRestrictions=0x${active.toString(16)}, moving=$moving")
    }
}

ビルド設定

  • android.car はシステム API のため useLibrary("android.car") を追加する
  • Manifest に uses-feature android.hardware.type.automotive を追加する
  • UX Restriction の読み取りに追加パーミッションは不要

もう一つのハマりどころ

ここまで実装して走行状態へ切り替えると、自作画面ではなく You can't use this feature while driving という AAOS 標準の画面が表示された。

blocked.png

ログは

moving=true

のまま更新されている。つまりアプリ自体は動作しており、表示だけが AAOS に覆われていた。

原因は distractionOptimized を宣言していなかったことだった。

<meta-data
    android:name="distractionOptimized"
    android:value="true" />

これを追加すると自前画面が表示されるようになる。

設定 走行中
true 自前画面
false AAOS ブロック画面

distractionOptimized は「アプリを動かすか」ではなく、「走行中でも自前 UI を表示する前提で設計されていること」を宣言するための属性である。

なお、量産車では OEM 側の審査やホワイトリスト登録も別途必要になる。Manifest の変更は再インストールしないと反映されない点にも注意したい。

メモ

記事用のスクリーンショットを撮影していると、Emulator 固有の問題にも遭遇した。AAOSのEmulatorがマルチディスプレイ構成のため、

adb exec-out screencap -p

の出力先頭に警告文字列が混ざり、PNG が壊れてしまった。
このような不具合が発生する場合には、PNG シグネチャ以降だけを書き出せば復元できる。

adb exec-out screencap -p > raw.bin
python3 -c 'import sys;d=open("raw.bin","rb").read();i=d.find(b"\x89PNG\r\n\x1a\n");open("shot.png","wb").write(d[i:])'

まとめ

  • UX_RESTRICTIONS_NO_VIDEO の切り替えは速度ではなくギアに依存
  • 車両状態をアプリ側で独自に判定するよりも CarUxRestrictionsManager が提供する UX Restriction を利用した方が AAOS の判定と一致
  • AAOS Emulator では inject-vhal-event を利用して実機なしで挙動を検証可能
  • 走行中も自前画面を表示するには distractionOptimized の宣言が必要

参照

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?