В прошлой статье мы запустили эмулятор и убедились, что Android Automotive OS (AAOS) — это не просто зеркало вашего телефона. Это самостоятельная экосистема. Но пока мы запускали только стандартные медиа-плееры, мы скользили по поверхности.

Настоящая магия начинается там, где софт встречается с «железом».

В телефоне у вас есть стандартный набор датчиков: GPS, акселерометр, гироскоп. В современном автомобиле таких датчиков тысячи: от давления в шинах и температуры масла до угла поворота руля и статуса каждой двери. Как Android-разработчику получить доступ к этому океану данных? Не будем же мы тянуть провода к OBDII разъему?

Конечно, нет. Для этого Google построила мощный мост, по которому мы сегодня и пройдемся. Добро пожаловать в мир Car API и VHAL.

Чтобы понять, как прочитать скорость автомобиля или датчик освещенности, нужно взглянуть на архитектуру AAOS. Она напоминает слоеный пирог, где каждый слой отвечает за свой уровень абстракции.

head unit BMW iX3 2026
head unit BMW iX3 2026

Давайте разберем этот стек сверху вниз:

  1. Car Apps: Это ваши приложения. Они живут в Java/Kotlin мире и ничего не знают о физике автомобиля.

  2. Car API: Библиотека от Google или местами собственная от автопроизводителя (android.car.*). Это наша коробка с инструментами. Она предоставляет высокоуровневые менеджеры (например, CarPropertyManager) для общения с системой.

  3. CarService: Системный демон. Это сердце AAOS. Он агрегирует данные, управляет правами доступа и пересылает команды ниже.

  4. Vehicle HAL (VHAL): Самый интересный рубеж. Железный

VHAL: Переводчик с «автомобильного» на Android

Нижний уровень — это Vehicle Hardware Abstraction Layer. Автопроизводители (OEM) используют разные шины данных (CAN, LIN, Automotive Ethernet) и разные протоколы. Если бы Android пытался понять каждый из них, он бы сошел с ума (еще больше чем сейчас).

VHAL — это контракт, которые так любит Google и мало уважают потребители его технологий. Android говорит производителю железа: «Мне всё равно, как ты меряешь скорость, просто отдай мне её в формате Float через вот этот интерфейс». VHAL берет сырые данные из CAN-шины и упаковывает их в понятные для Android свойства (Properties).

Практика: Читаем данные сенсоров

Хватит теории. Давайте напишем код, который делает что-то полезное. Например, узнаем текущую скорость автомобиля и температуру в салоне.

В центре всего стоит класс CarPropertyManager. Он работает по принципу Pub/Sub (издатель/подписчик). Мы подписываемся на ID свойства и ждем обновлений.

Шаг 1: Подключение к CarService

Сначала нам нужно создать экземпляр объекта Car и подключиться к сервису.

import android.car.Car
import android.content.ComponentName
import android.content.ServiceConnection
import android.os.IBinder

// В вашей Activity или ViewModel
private lateinit var car: Car
private var carPropertyManager: CarPropertyManager? = null

private val serviceConnection = object : ServiceConnection {
    override fun onServiceConnected(name: ComponentName, service: IBinder) {
        // Сервис подключен, можно получать менеджеры
        carPropertyManager = car.getCarManager(Car.PROPERTY_SERVICE) as CarPropertyManager
        subscribeToSpeed()
    }

    override fun onServiceDisconnected(name: ComponentName) {
        carPropertyManager = null
    }
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Инициализация подключения. Важно: используйте applicationContext
    if (packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
        car = Car.createCar(this, serviceConnection)
    }
}

Шаг 2: Подписка на данные (Спидометр)

У каждого свойства в автомобиле есть уникальный ID, определенный в классе VehiclePropertyIds. Нам нужен PERF_VEHICLE_SPEED.

import android.car.hardware.CarPropertyValue
import android.car.hardware.property.CarPropertyManager
import android.car.VehiclePropertyIds

private fun subscribeToSpeed() {
    val speedCallback = object : CarPropertyManager.CarPropertyEventCallback {
        // Вызывается при изменении значения
        override fun onChangeEvent(event: CarPropertyValue<Any>) {
            // В документации сказано, что скорость приходит в м/с (Float)
            val speedInMetersPerSec = event.value as Float
            val speedKmH = speedInMetersPerSec * 3.6
            
            Log.d("CarDashboard", "Текущая скорость: ${speedKmH.toInt()} км/ч")
            // Тут можно обновить UI (не забудьте про MainThread!)
        }

        // Вызывается, если сенсор отвалился или возникла ошибка
        override fun onErrorEvent(propId: Int, zone: Int) {
            Log.e("CarDashboard", "Ошибка чтения сенсора: $propId")
        }
    }

    // Регистрация коллбека
    // SENSOR_RATE_NORMAL — частота обновлений (примерно 5-10 Гц)
    carPropertyManager?.registerCallback(
        speedCallback,
        VehiclePropertyIds.PERF_VEHICLE_SPEED,
        CarPropertyManager.SENSOR_RATE_NORMAL
    )
}

Зоны: Когда "Окно" — это не Windows

Автомобиль — сложная штука. У него, например, не одно окно, а четыре. Или климат-контроль может быть раздельный (для водителя и пассажира). В AAOS это решается через понятие AreaId (Zone).

Если вы хотите опустить стекло пассажира, вы не можете просто сказать «Опусти стекло». Вы должны указать какое.

// Пример: Получаем статус окна (открыто/закрыто) для переднего пассажира
val passengerWindowZone = VehicleAreaWindow.WINDOW_ROW_1_RIGHT

val windowState = carPropertyManager?.getIntProperty(
    VehiclePropertyIds.WINDOW_POS, 
    passengerWindowZone
)

Именно VHAL маппит константу WINDOW_ROW_1_RIGHT на конкретный физический контр��ллер нужной двери.

Безопасность и Permissions

Вы не можете просто так взять и прочитать данные машины. Это вопрос физической безопасности. Если ваше приложение начнет спамить в CAN-шину, это может быть опасно для жизни.

В AndroidManifest.xml нужно запрашивать специальные права. И в отличие от телефона, многие из них имеют уровень защиты signature или privileged. Это значит, что обычное приложение из Play Store их получить не сможет.

Для чтения скорости нам нужно:

<uses-permission android:name="android.car.permission.CAR_SPEED" />

Для сторонних разработчиков (3rd party) Google открывает только безопасный набор API (например, Car App Library), где прямой доступ к сенсорам скрыт. Но если вы пишете софт для OEM или системное приложение — перед вами открыт весь автомобиль.

Суровая реальность: Vendor Extensions

В теории все выглядит гладко: есть стандартный список VehiclePropertyIds, который описан в документации Google. Но реальный мир автопрома гораздо сложнее.

Что если у вашей машины есть функция массажа кресел, пневмоподвеска с пятью режимами или специфическая система ионизации воздуха? Стандартный Android про такие изыски может не знать.

Здесь на сцену выходят Vendor Extensions. OEM-производители (автоконцерны) имеют право расширять стандартный VHAL своими собственными свойствами.

  • В коде вы часто встретите ID, которых нет в официальной документации Google.

  • Многие компании пишут свои собственные обертки над CarPropertyManager, чтобы удобнее работать с этой спецификой.

  • Иногда кастомизируется даже сама библиотека android.car.

Так что, приходя в новую компанию, не удивляйтесь, если StackOverflow вам не поможет. Вам придется читать внутреннюю документацию инженеров, которые проектировали "железо" этой конкретной модели.

Заключение

Мы только что заглянули под капот Android Automotive. Мы увидели, что за привычным Android API скрывается мощная система CarService и VHAL, которая превращает сигналы железа в объекты Java/Kotlin.

Мы поняли, что AAOS — это не коробочное решение, а конструктор, который каждый автопроизводитель собирает под себя.

Что дальше? Мы научились получать данные (даже кастомные). Но просто вывести цифру «120 км/ч» на экран недостаточно. В следующей статье мы поговорим о Driver Distraction Guidelines: как проектировать интерфейсы, которые не убивают, и почему в автомобиле нельзя использовать маленькие кнопки.

Спойлер: Вам придется забыть половину того, что вы знали о UX на смартфонах.