Driver Distractionに対応するためのAndroid Automotive OSの仕組みを見てみる

# Driver Distractionに対応するためのAndroid Automotive OSの仕組みを見てみる

本記事では、Android Automotive OS (AAOS)において車が走行中であるかの判定方法や、走行中にUXを制限する方法についてAndroid Open Source Project (AOSP)のコードを確認しながら説明していきます。
また、どのようにUxRestrictionsのルールを組み込むか、それをどのように利用するかも確認します。

※本記事で参照しているのはAOSPのコードであり、具体的な製品の仕様とは異なる可能性がありますのでご承知おきください。

# 前提知識
車両向けアプリを開発するにあたって必要となる考え方についてここで説明します。

## Driver Distractionとは

車載向けアプリ（特にAAOS向け）の開発において考慮しなければならない特徴的な要件の一つがDriver Distractionです。
Driver Distractionとは、ドライバーの運転に対する注意が散漫になることです。
運転は人命に関わる重大なタスクであるため、アプリはドライバーの集中を妨げないようにする必要があります。そのために車載アプリのUI/UXで守るべきルールが存在します。

ここでHondaのDriver Distractionに対応するためのルールを述べることはできませんが、Googleの以下のドキュメントに基本的な考え方が書かれています。

[Design for Driving](https://developers.google.com/cars/design?hl=ja)

> アプリのコンテンツと操作は、ドライバーの注意散漫を最小限に抑えながら、運転中の操作を補完するものである必要があります。ドライバーが道路から目を離さず、ハンドルを握ったまま操作できるように、UI を簡素化する必要があります。

さらに、[操作の原則](https://developers.google.com/cars/design/design-foundations/interaction-principles?hl=ja)、[ビジュアルの原則](https://developers.google.com/cars/design/design-foundations/visual-principles?hl=ja)、[記述の原則](https://developers.google.com/cars/design/design-foundations/writing-principles?hl=ja)ではより具体的に原則について説明しており、参考になります。

## Driver Distractionに対応するUI/UX

Driver Distractionを最小化するために、アプリのUI/UXを制限することがあります。
特に走行中については厳しい制限が適用されます。

UI/UXの制限の例としては以下のようなものが挙げられます。

- 操作の制限
  - スクロール操作の制限
  - キーボード入力など複雑な操作の禁止
  - アプリの階層遷移の制限
- ビジュアルの制限
  - 画面に表示する要素の数
  - 文字サイズやボタンサイズの制限（一定以上の大きさ）
  - 注意を引く動画やアニメーションの再生禁止
  - テキストの長さの制限

走行中の利用も想定する車載向けアプリを開発する場合にはこれらをはじめ、ドライバーの状況を想定してUI/UXを作り込んでいかなければなりません。

# AAOSのUX Restrictionの実装

AAOSではどのようにUI/UX制限（UX Restriction）のルールを定義し、Androidアプリ側にどのように提供しているかを見ていきます。

## テンプレートによるUI/UX制限の実現

車載向けアプリに対するノウハウがないアプリ提供者にとって、Driver Distractionに対応したUIを考えることは困難です。一方で、ナビやメディアなど運転中に利用できることでユーザーの利便性を上げられるアプリもあります。

そこでGoogleは[車載向けにリリースできるアプリのタイプ](https://developers.google.com/cars/design/create-apps/app-types/overview?hl=ja)を定め、同時に[Android Autoアプリ向けにUIのテンプレート](https://developers.google.com/cars/design/create-apps/apps-for-drivers/overview?hl=ja)を用意しています。

これらのテンプレートはAAOSを開発している自動車メーカーによって、車載アプリとして適したUIにカスタマイズされています。また、走行中かどうかを判定してUIを切り替えるものもあります。

そのためテンプレートを使用することで、アプリ提供者は少ない労力で車載利用に最適化された方法でUIを実現できます。

## Android Automotive OSのDriver Distraction（UX Restriction）

AAOSでは車両が走行中かどうか判定し、状況に応じてUI/UXを制限する仕組みがあります。  
ここではそのルールの定義、走行状態の判定、利用方法を見ていきます。

Googleが定義してくれているテンプレートを利用する場合はここまで意識することはないと思いますが、どのような仕組みになっているか見てみるのは面白いと思います。

### UxRestrictionsのルールの定義

Android Automotive OSを開発する自動車メーカーは、[packages/services/Car/service/res/xml/car_ux_restrictions_map.xml](https://cs.android.com/android/platform/superproject/main/+/main:packages/services/Car/service/res/xml/car_ux_restrictions_map.xml)
を使用してUX制限のルールを記載します（リンクはAOSPのデフォルトのファイルです）。  
例えば以下の例では、movingの状態で5.0m/s以上の速度が出ている場合にUX制限を適用しています。

```xml
<!-- Restrictions for speed >=5 -->
<DrivingState state="moving" minSpeed="5.0">
    <Restrictions requiresDistractionOptimization="true" uxr="no_dialpad|no_filtering|limit_string_length|no_keyboard|no_video|limit_content|no_setup|no_text_message"/>
</DrivingState>
```

UX制限の種別は[CarUxRestrictions](https://cs.android.com/android/platform/superproject/main/+/main:packages/services/Car/car-lib/src/android/car/drivingstate/CarUxRestrictions.java;drc=5d7a5176c4b9ed7cb19a7b99c42b536ac62624c7;l=69)に定義があります。
例えば、`no_video`=`UX_RESTRICTIONS_NO_VIDEO`の場合は動画を制限することを意図します。

`limit_content`=`UX_RESTRICTIONS_LIMIT_CONTENT`は画面に含まれるコンテンツの数を制限します。
`getMaxCumulativeContentItems()`や`getMaxContentDepth()`で取得できる値によって、アプリ側でUIが制限するものですが、この値は同じく[packages/services/Car/service/res/xml/car_ux_restrictions_map.xml](https://cs.android.com/android/platform/superproject/main/+/main:packages/services/Car/service/res/xml/car_ux_restrictions_map.xml)内の`RestrictionParameters`に定義します。

```xml
<!-- Configure restriction parameters here-->
<RestrictionParameters>
    <!-- Parameters to express displayed String related restrictions -->
    <!-- Max allowed length of general purpose strings when limit_string_length is imposed-->
    <StringRestrictions maxLength="120"/>
    <!-- Parameters to express content related restrictions -->
    <!-- Max number of cumulative content items allowed to be displayed when
    limit_content is imposed. -->
    <!-- Maximum levels deep that the user can navigate to when limit_content is imposed. -->
    <ContentRestrictions maxCumulativeItems="21" maxDepth="3"/>
</RestrictionParameters>
```

### DrivingStateの判定処理

DrivingStateは下記の通りです。
- parked: ギアがパーキングに入っている
- idling: ギアがパーキングに入っておらず、速度がゼロ
- moving: ギアがパーキングに入っておらず、速度がゼロではない

以下はその判定ロジックの抜粋です：

```java
/**
 * Infers the current driving state of the car from the other Car Sensor properties like
 * Current Gear, Speed etc.
 *
 * @return Current driving state
 */
@GuardedBy("mLock")
@CarDrivingState
private int inferDrivingStateLocked() {
    updateVehiclePropertiesIfNeededLocked();
    if (DBG) {
        Slogf.d(TAG,
                "inferDrivingStateLocked mLastGear: " + mLastGear + "mLastSpeed: " + mLastSpeed
                        + "mLastIgnitionState: " + mLastIgnitionState);
    }

    /*
        Logic to start off deriving driving state:
        1. If gear == parked, then Driving State is parked.
        2. If gear != parked,
                2a. if parking brake is applied, then Driving state is parked.
                2b. if parking brake is not applied or unknown/unavailable, then driving state
                is still unknown.
        3. If driving state is unknown at the end of step 2,
            3a. if speed == 0, then driving state is idling
            3b. if speed != 0, then driving state is moving
            3c. if speed is unavailable,
                3ca. If ignition state is supported and ignition state == lock or acc or
                off, then driving state is parked
                3cb. else driving state is unknown
        */

    if (isVehicleKnownToBeParkedLocked()) {
        return CarDrivingStateEvent.DRIVING_STATE_PARKED;
    }

    // We don't know if the vehicle is parked, let's look at the speed.
    if (!isPropertyStatusAvailable(mLastSpeed)) {
        if (isOptionalPropertySupportedLocked(VehicleProperty.IGNITION_STATE)
                && isVehicleKnownToBeInLockOrAccOrOffIgnitionStateLocked()) {
            return CarDrivingStateEvent.DRIVING_STATE_PARKED;
        }
        return CarDrivingStateEvent.DRIVING_STATE_UNKNOWN;
    } else if (mLastSpeed.getValue().equals(0f)) {
        return CarDrivingStateEvent.DRIVING_STATE_IDLING;
    } else {
        return CarDrivingStateEvent.DRIVING_STATE_MOVING;
    }
}
```

それぞれの状態でどのようなUX制限（Restrictions）のルールを適用するかをcar_ux_restrictions_map.xmlに記載します。  
DrivingStateの定義は[CarDrivingStateEvent](https://cs.android.com/android/platform/superproject/main/+/main:packages/services/Car/car-lib/src/android/car/drivingstate/CarDrivingStateEvent.java;drc=76c9b1bcee8ffe3aa3b98070b53613c13352d1f6;l=64)にあります。
[CarDrivingStateService](https://cs.android.com/android/platform/superproject/main/+/main:packages/services/Car/service/src/com/android/car/CarDrivingStateService.java;drc=61197364367c9e404c7da6900658f1b16c42d0da;l=61)がギア、パーキングブレーキ、速度、イグニッションなどの状態を持っており、[CarDrivingStateServiceのinferDrivingStateLocked](https://cs.android.com/android/platform/superproject/main/+/main:packages/services/Car/service/src/com/android/car/CarDrivingStateService.java;drc=61197364367c9e404c7da6900658f1b16c42d0da;l=452)の中でDrivingStateを判定しています。  

### UxRestrictionの判定処理

car_ux_restrictions_map.xmlで定義した内容が読み込まれ保持されており、[CarUxRestrictionsConfigurationのgetUxRestrictions](https://cs.android.com/android/platform/superproject/main/+/main:packages/services/Car/car-lib/src/android/car/drivingstate/CarUxRestrictionsConfiguration.java;drc=61197364367c9e404c7da6900658f1b16c42d0da;l=163)でDrivingStateと速度に対応するUxRestrictionsを判定します。
[CarUxRestrictionManagerService](https://cs.android.com/android/platform/superproject/main/+/main:packages/services/Car/service/src/com/android/car/CarUxRestrictionsManagerService.java;drc=61197364367c9e404c7da6900658f1b16c42d0da;l=998)で車両情報（速度）とDrivingStateを監視し、登録されたCarUxRestrictionsChangeListenerのonUxRestrictionsChangedを呼び出しています。

利用側では、[運転状態と UX 制限の利用](https://source.android.com/docs/automotive/driver_distraction/consume?hl=ja)にあるとおり、`CarUxRestrictionManager.OnUxRestrictionsChangedListener`を実装して`registerListener`することで、現在のCarUxRestrictionsを受け取って制限に応じた処理を行うことができます。

```java
@Nullable private CarUxRestrictionsManager mCarUxRestrictionsManager;
private CarUxRestrictions mCurrentUxRestrictions;

/* Implement the onUxRestrictionsChangedListener interface */
private CarUxRestrictionsManager.OnUxRestrictionsChangedListener mUxrChangeListener =
            new CarUxRestrictionsManager.OnUxRestrictionsChangedListener()
    {
        @Override
        public void onUxRestrictionsChanged(CarUxRestrictions carUxRestrictions) {
            mCurrentUxRestrictions = carUxRestrictions;
            /* Handle the new restrictions */
            handleUxRestrictionsChanged(carUxRestrictions);
        }
    };

```


# さいごに

本記事では`car_ux_restrictions_map.xml`でのUxRestrictionsの定義から、それがアプリでの`onUxRestrictionsChanged`呼び出しまでつながってくる流れをかいつまんで確認しました。

AAOSやAAOS向けのアプリ開発では、一般のAndroidアプリ開発とは異なる車両特有の考慮が必要です。
車両の状態に応じてUIを適切に制限することで、安全性を確保しながらもユーザーに価値を提供するアプリを開発することができます。
これは自動車メーカーならではの知識と技術が活かせる領域であり、車載ソフトウェアの開発の醍醐味と言えます。

---
※この記事は、本田技研工業のデジタルサービス ソフトウェア内製化チームによる寄稿です。  
[他の記事はこちら](https://qiita.com/organizations/honda-motor/items)からご覧いただけます。