Integration guide for OEMs

This page describes how to process rotary inputs in the VHAL, configure your build to include the rotary service, and how to customize the rotary experience across all apps. For preinstalled OEM apps, such as an OEM-provided launcher, see Car UI Library (car-ui-library).

VHAL

A rotary controller supports the following actions:

  • Nudge up, down, left, and right.
  • Rotate clockwise and counterclockwise.
  • Press the Center button.
  • Press the Back button.
  • Press the Home button.
  • Press other buttons, such as Phone and Media.

See hardware/interfaces/automotive/vehicle/2.0/types.hal for documentation on the system properties and corresponding int32Values.

The VHAL should handle these actions:

Nudge

When the user pushes the rotary controller right, the VHAL should use the HW_KEY_INPUT property with the following int32Values to send an event to Android:

  1. ACTION_DOWN
  2. KEYCODE_SYSTEM_NAVIGATION_RIGHT
  3. Target display.

When the user releases the rotary controller, the VHAL should use the same property and keycode with ACTION_UP. Nudges in other directions should use the corresponding keycodes.

There aren't keycodes for diagonals but the VHAL can combine a horizontal and vertical event to produce a diagonal if the hardware supports diagonals. For example, nudging up and to the left should produce:

  • HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_LEFT ACTION_DOWN
  • HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_UP ACTION_DOWN

In either order (and subsequently) releasing the rotary controller should produce:

  • HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_LEFT ACTION_UP
  • HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_UP ACTION_UP

The user may push the rotary controller in a perpendicular direction before releasing it. For example, the following scenario:

Perpendicular direction
Figure 1. Perpendicular direction

This should generate the following sequence of events:

  1. HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_LEFT ACTION_DOWN
  2. HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_UP ACTION_DOWN
  3. HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_LEFT ACTION_UP
  4. HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_UP ACTION_UP

No repeat events should be generated while the rotary controller is held in one direction.

Rotate

When the user rotates the rotary controller clockwise by one detent (click), the VHAL should use the HW_ROTARY_INPUT property with the following int32Values to send an event to Android:

  1. ROTARY_INPUT_TYPE_SYSTEM_NAVIGATION
  2. One (1) detent.
  3. Target display.

The timestamp of the event should be set to the elapsed time in nanoseconds.

A one (1) detent counterclockwise rotation should generate the same event but with -1 for the number of detents.

If multiple detents of rotation in the same direction occur in rapid succession, the VHAL should combine the detents into a single event so as not to overload the system with events. In this case, the timestamp of the event should be when the first detent of rotation occurred. The int32Values should include the number of nanoseconds between consecutive detents of rotation.

For example, the following sequence of rotations:

  • At time t0, the user rotated one detent counterclockwise.
  • At time t0 + 5 ns, the user rotated one detent counterclockwise.
  • At time t0 + 8 ns, the user rotated one detent counterclockwise.

should generate this event:

  • Property: HW_ROTARY_INPUT
  • Timestamp: t0
  • int32Values:
    1. ROTARY_INPUT_TYPE_SYSTEM_NAVIGATION
    2. -3 (three detents counterclockwise).
    3. Target display.
    4. 5 ns between first and second detent.
    5. 3 ns between second and third detent.

Center button

When the user presses the Center button, the VHAL should use the HW_KEY_INPUT property with the following int32Values to send an event to Android:

  1. ACTION_DOWN
  2. KEYCODE_DPAD_CENTER
  3. Target display.

When the user releases the rotary controller, the VHAL should use the same property and keycode with ACTION_UP.

Do not generate repeat events when the Center button is held down.

Back button

When the user presses the Back button, the VHAL should use the HW_KEY_INPUT property with the following int32Values to send an event to Android:

  1. ACTION_DOWN
  2. KEYCODE_BACK
  3. Target display.

When the user releases the rotary controller, the VHAL should use the same property and keycode with ACTION_UP.

No repeat events should be generated while the Center button is held down.

Home button

Handle the Home button as you would the Back button but with KEYCODE_HOME instead of KEYCODE_BACK.

Other buttons

If the rotary controller includes any additional buttons, the VHAL can handle them however the OEM likes as they aren't considered part of rotary from the perspective of Android. These are typically handled like the Back and Home buttons but with different keycodes. For example, KEYCODE_CALL or KEYCODE_MUSIC.

Build configuration

Rotary navigation is provided by an accessibility service called RotaryService. To include this service in the system image for your device, add the following line to your makefile:

PRODUCT_PACKAGES += CarRotaryController

You may also want to include the follow packages in debug builds:

The rotary service is enabled automatically when the device boots and when a user switch occurs. This ensures that the user can use the rotary controller during set-up.

If you use the same build for cars with and without a rotary controller, add CarRotaryController as shown above so that the necessary code is included in the build. To prevent the rotary service from being enabled on non-rotary cars, create a static RRO to overlay the rotaryService string resource in packages/services/Car/service with an empty string. You'll use the same build, but have separate product configurations, for rotary and non-rotary devices. Only the latter includes the overlay.

Customization

OEMs can customize focus finding logic, the focus highlight, and some additional items through resource overlays in the following locations:

  • car-ui-library is located in packages/apps/Car/libs/car-ui-lib
  • RotaryService is located in packages/apps/Car/RotaryController
  • Core is located in frameworks/base/core

Nudge history

The OEM can configure whether or not each of two types of nudge history is enabled and, if so, the cache size and expiration policy. This is all done by overriding various car-ui-library resources.

Focus history cache

(Android 11 QPR3, Android 11 Car, Android 12)
This per-FocusArea cache stores the most recently focused view within the FocusArea so that it can be focused when nudging back to the FocusArea. This cache can be configured by overlaying the following car-ui-library resources:

  • car_ui_focus_history_cache_type:
    1. Cache is disabled.
    2. Cache will expire after some time (see below).
    3. Cache will never expire.
  • car_ui_focus_history_expiration_period_ms: How many milliseconds before the cache expires if the cache type is set to two (2) (see above).

FocusArea history cache

(Android 11 QPR3, Android 11 Car, Android 12)
This cache stores a history of nudges so that nudging in the opposite direction can return focus to the same FocusArea. This cache can be configured by overlaying the following car-ui-library resources:

  • car_ui_focus_area_history_cache_type:
    1. Cache is disabled.
    2. Cache expires after some time (see below).
    3. Cache never expires.
  • car_ui_focus_area_history_expiration_period_ms: How many milliseconds before the cache expires if the cache type is set to 2 (see above).
  • car_ui_clear_focus_area_history_when_rotating: Whether to void the cache when the user rotates the controller.

Rotation

(Android 11 QPR3, Android 11 Car, Android 12)
The OEM can override two integer resources in the RotaryService to specify whether there is acceleration, such as mouse acceleration, for rotation:

  • rotation_acceleration_3x_ms: Time interval (in milliseconds) used to decide whether Google should accelerate the controller rotation for a detent of rotation. If the interval between this detent and the previous detent of rotation is smaller than this value, it will be treated as three detents of rotation. Set this to 2147483647 to disable 3× acceleration.
  • rotation_acceleration_2x_ms: Similar to rotation_acceleration_3x_ms. Used for 2× acceleration. Set this to 2147483647 to disable 2× acceleration.

Acceleration works best when there are individual timestamps for each detent of rotation, as required by the VHAL. If these aren't available, the RotaryService assumes that the detents of rotation are evenly spaced.

/**
     * Property to feed H/W rotary events to android
     *
     * int32Values[0] : RotaryInputType identifying which rotary knob rotated
     * int32Values[1] : number of detents (clicks), positive for clockwise,
     *                  negative for counterclockwise
     * int32Values[2] : target display defined in VehicleDisplay. Events not
     *                  tied to specific display must be sent to
     *                  VehicleDisplay#MAIN.
     * int32values[3 .. 3 + abs(number of detents) - 2]:
     *                  nanosecond deltas between pairs of consecutive detents,
     *                  if the number of detents is > 1 or < -1
     *
     * VehiclePropValue.timestamp: when the rotation occurred. If the number of
     *                             detents is > 1 or < -1, this is when the
     *                             first detent of rotation occurred.
     *
     * @change_mode VehiclePropertyChangeMode:ON_CHANGE
     * @data_enum RotaryInputType
     * @access VehiclePropertyAccess:READ
     */
    HW_ROTARY_INPUT = (
        0x0A20
        | VehiclePropertyGroup:SYSTEM
        | VehiclePropertyType:INT32_VEC
        | VehicleArea:GLOBAL),

Focus highlight

The OEM can override the default focus highlight in the Android framework and several focus highlight resources in car-ui-library.

Default focus highlight

The Android framework provides a default focus highlight through the attribute selectableItemBackground. In Theme.DeviceDefault, this attribute refers to item_background.xml in Core. The OEM can overlay item_background.xml to change the default focus highlight drawable.

This drawable should typically be a StateListDrawable, which adjusts the background based on different combinations of states, including android:state_focused and android:state_pressed. When the user uses the rotary controller to focus a view, android:state_focused will be true, but android:state_pressed will be false. If the user then presses the Center button on the rotary controller, both android:state_focused and android:state_pressed will be true while the user holds the button down. When the user releases the button, only android:state_focused will remain true.

car-ui-library uses a theme derived from Theme.DeviceDefault. As a result, this overlay affects apps that use this library and apps that use any theme derived from Theme.DeviceDefault. It won't affect apps that use an unrelated theme, such as Theme.Material.

Focus highlight resources in car-ui-library

The OEM can override several car-ui-library resources to control how the focus highlight looks on views with a non-rectangular (such as round or pill-shaped) focus highlight and in apps that use a theme that doesn't derive from Theme.DeviceDefault. These resources should be overlaid so that the focus highlight is consistent with the default focus highlight drawable.

(Android 11 QPR3, Android 11 Car, Android 12)
The following resources are used to indicate when a view is focused but not pressed:

  • car_ui_rotary_focus_fill_color: Fill color.
  • car_ui_rotary_focus_stroke_color: Outline color.
  • car_ui_rotary_focus_stroke_width: Thickness of the outline.

(Android 11 QPR3, Android 11 Car, Android 12)
The following resources are used to indicate when a view is focused and pressed:

  • car_ui_rotary_focus_pressed_fill_color: Fill color.
  • car_ui_rotary_focus_pressed_stroke_color: Outline color.
  • car_ui_rotary_focus_pressed_stroke_width: Thickness of the outline.

Sometimes a button is given a solid background color to bring it to the user's attention, as in the example shown. This may make the focus highlight difficult to see.

Button with solid background
Figure 2. Button with solid background

In this situation, the developer can specify a custom focus highlight using secondary colors:
  • (Android 11 QPR3, Android 11 Car, Android 12)
    car_ui_rotary_focus_fill_secondary_color
    car_ui_rotary_focus_stroke_secondary_color
  • (Android 12)
    car_ui_rotary_focus_pressed_fill_secondary_color
    car_ui_rotary_focus_pressed_stroke_secondary_color

Any of the colors can be transparent and either dimension can be zero if, for example, you wanted only a fill or only an outline.

FocusArea highlight

(Android 11 QPR3, Android 11 Car, Android 12)
FocusArea can draw two types of highlight when one of its descendants is focused. Both can be used in conjunction, if desired. This feature is disabled by default in AOSP, but can be enabled by overriding car-ui-library resources:

  • car_ui_enable_focus_area_foreground_highlight: Draw a highlight on top of the FocusArea and its descendants. In AOSP, this drawable is an outline around the FocusArea. OEMs can override the car_ui_focus_area_foreground_highlight drawable.
  • car_ui_enable_focus_area_background_highlight: Draw a highlight on top of the FocusArea but behind its descendants. In AOSP, this drawable is a solid fill. OEMs can override the car_ui_focus_area_background_highlight drawable.

Input Method Editors

Input Method Editors (IME) are input methods. For example, an on-screen keyboard.

(Android 11 QPR3, Android 11 Car, Android 12)
The OEM must overlay the default_touch_input_method string resource in the RotaryService to specify the ComponentName of the touch-based IME. For example, if the OEM uses the IME provided with Android Automotive, they should specify com.google.android.apps.automotive.inputmethod/.InputMethodService.

(Android 11 QPR3, Android 11 Car, Android 12)
If the OEM has created an IME specifically for rotary, they should specify its ComponentName in the rotary_input_method resource. If this resource is overlaid, the specified IME is used whenever the user is interacting with the head unit through the rotary controller's nudge, rotation, and Center button. When the user touches the screen, the previous IME will be used. The Back button (and other buttons on the rotary controller) have no effect on IME selection. If this resource isn't overlaid, no IME switching occurs. Carboard does not support rotary so the user can't enter text through the rotary controller if the OEM hasn't provided a rotary IME.

RotaryIME is a demo rotary IME. While basic, it's sufficient to try out the automatic IME switching described above. The source code for RotaryIME can be found in packages/apps/Car/tests/RotaryIME/.

Off-screen nudges

By default, when the user tries to nudge off the edge of the screen, nothing happens. The OEM can configure what should occur for each of the four directions by specifying any combination of:

  1. A global action defined by AccessibilityService. For example, GLOBAL_ACTION_BACK.
  2. A key code, such as KEYCODE_BACK.
  3. An intent to launch an activity represented as a URL.

(Android 11 QPR3, Android 11 Car, Android 12)
These are specified by overlaying the following array resources in the RotaryService:

  • off_screen_nudge_global_actions: Array of global actions to perform when the user nudges up, down, left, or right off the edge of the screen. No global action is performed if the relevant element of this array is -1.
  • off_screen_nudge_key_codes: Array of key codes of click events to inject when the user nudges up, down, left, or right off the edge of the screen. No events are injected if the relevant element of this array is 0 (KEYCODE_UNKNOWN).
  • off_screen_nudge_intents: Array of intents to launch an activity when the user nudges up, down, left, or right off the edge of the screen. No activity is launched if the relevant element of this array is empty.

Other configurations

You should overlay the following RotaryService resources:

  • (Android 11 QPR3, Android 11 Car, Android 12)
    config_showHeadsUpNotificationOnBottom: Boolean value to represent whether heads-up notifications should be shown on the bottom as opposed to the top. This must have the same value as the config_showHeadsUpNotificationOnBottom Boolean resource in frameworks/base/packages/CarSystemUI/res/values/config.xml
  • (Android 11 QPR3, Android 11 Car, Android 12)
    notification_headsup_card_margin_horizontal: Left and right margin for heads-up notification window. This must have the same value as the notification_headsup_card_margin_horizontal dimen resource in packages/apps/Car/Notification/res/values/dimens.xml
  • (Android 12)
    excluded_application_overlay_window_titles: An array of titles of windows that shouldn't be considered overlay windows. This should include titles of app windows that represent TaskViews or TaskDisplayAreas. By default, this list contains only "Maps."

You can overlay the following RotaryService resource:

  • (Android 11 QPR3, Android 11 Car, Android 12)
    long_press_ms: Integer value to represent how many milliseconds the Center button must be held down to trigger a long-press. Zero indicates the system default long-press timeout should be used. This is the default value.