Create a Custom VPA Adaptation Service

Last edit: 2023.07.31

The VPA Adaptation service provides an abstraction for a VPA engine, with which other TomTom Digital Cockpit components communicate.

The TomTom Digital Cockpit platform includes a stock implementation of the VPA Adaptation Service for the Alexa Auto VPA, and support for the Cerence Assistant VPA is in development. If TomTom Digital Cockpit does not support a specific VPA, or its stock implementation is not suitable for the customer's use case, you can create your own VPA Adaptation Service.

Example VPA app

The TomTom Digital Cockpit SDK comes with an example app showing how to create a custom VPA Adaptation Service. See the examples/vpa directory.

Create a custom VPA Adaptation Service

The following sections describe how to create a custom VPA Adaptation service implementation.

Create a service implementation module

The new module depends on VpaAdaptationService, declared in the package com.tomtom.ivi.platform.vpa.api.service.vpaadaptation.

/build-logic/libraries.versions.toml

iviPlatformVpaApiServiceVpaadaptation = { module = "com.tomtom.ivi.platform:platform_vpa_api_service_vpaadaptation", version.ref = "iviPlatform" }

build.gradle.kts

1dependencies {
2 implementation(libraries.iviPlatformVpaApiServiceVpaadaptation)
3}

The VpaAdaptationService API is experimental, and has to be explicitly opted in.

build.gradle.kts

1ivi {
2 optInToExperimentalApis = true
3}

Implement the VPA Adaptation Service

The VPA Adaptation service can be implemented by defining a class that inherits from the abstract VpaAdaptationServiceBase class and implements the methods defined in the VpaAdaptationService interface.

The service implementation requires a number of properties to be set for configuring itself with the TomTom Digital Cockpit platform. The properties can be set by overriding the onCreate() method:

src/main/kotlin/ExampleVpaService.kt

1override fun onCreate() {
2 super.onCreate()
3
4 vpaProperties = VpaProperties(
5 vpaIdentifier = VpaIdentifier(name = "Example VPA"),
6 supportedLocales = List<Locale> = listOf(Locale.US),
7 supportedCountries = List<String> = listOf("USA")
8 )
9
10 vpaAuthenticationStatus = VpaAuthenticationStatus(AuthenticationState.AUTHENTICATED)
11
12 vpaAvailabilityState = VpaAvailabilityState.AVAILABLE
13
14 dialogueState = DialogueState.IDLE
15
16 alerts = emptyList()
17
18 hasPendingNotifications = false
19
20 vpaSettings = VpaSettings(
21 wakeUpWordEnabled = false,
22 activeLocales = emptyList(),
23 enabledEarcons = EnumSet.noneOf(VpaEarconType::class.java),
24 doNotDisturbModeEnabled = false,
25 locationSharingEnabled = false,
26 syncVehicleNavigationFavoritesEnabled = false,
27 allowedPersonalData = emptyMap()
28 )
29
30 serviceReady = true
31}

Depending on the features supported by your VPA, you will need to override other functions of this service so that the VPA can react to certain actions by the user.

VPA Properties

Static information about the VPA, such as its name, supported locales and countries, are stored in vpaProperties.

src/main/kotlin/ExampleVpaService.kt

1vpaProperties = VpaProperties(
2 vpaIdentifier = VpaIdentifier(name = "Example VPA"),
3 supportedLocales = List<Locale> = listOf(Locale.US),
4 supportedCountries = List<String> = listOf("USA")
5)

Authentication Status

The current authentication state of the VPA is stored in vpaAuthenticationStatus. If the VPA supports user authentication, the logIn() and logOut() functions should be overridden to allow a client to authenticate with or log out of the VPA service. These functions should update the vpaAuthenticationStatus to reflect the new situation.

src/main/kotlin/ExampleVpaService.kt

1override fun onCreate() {
2 vpaAuthenticationStatus = VpaAuthenticationStatus(AuthenticationState.NOT_AUTHENTICATED)
3}
4
5override suspend fun logIn() {
6 // TODO: Log in to the VPA.
7
8 // If the login is successful - update the status.
9 vpaAuthenticationStatus = VpaAuthenticationStatus(AuthenticationState.AUTHENTICATED)
10}
11
12override suspend fun logOut() {
13 // TODO: Log out of the VPA.
14
15 // If the logout is successful - update the status.
16 vpaAuthenticationStatus = VpaAuthenticationStatus(AuthenticationState.NOT_AUTHENTICATED)
17}

Refer to VpaAuthenticationStatus for the details.

Availability State

Property vpaAvailabilityState indicates whether this VPA is ready to be used by clients.

src/main/kotlin/ExampleVpaService.kt

vpaAvailabilityState = VpaAvailabilityState.AVAILABLE

Dialogue State

The state of the conversation with the VPA is reflected by dialogueState. This helps a UI indicate whether, for example, the VPA is waiting for the user to reply to a question. It is essential for the service to monitor the VPA dialogue state and update the property accordingly.

src/main/kotlin/ExampleVpaService.kt

dialogueState = DialogueState.IDLE

Alerts

Property alerts is used to hold information about the alerts that have been scheduled through interaction with the VPA.

src/main/kotlin/ExampleVpaService.kt

alerts = emptyList()

Pending Notifications

Some of the VPA messages may not be played at once. Property hasPendingNotifications informs a client whether the VPA has voice notifications ready to be played.

src/main/kotlin/ExampleVpaService.kt

hasPendingNotifications = false

Settings

Property vpaSettings holds a number of settings of the VPA. The VpaAdaptationService provides functions to update these settings; please refer to the VpaAdaptationService API reference documentation for details.

src/main/kotlin/ExampleVpaService.kt

1override fun onCreate() {
2 vpaSettings = VpaSettings(
3 wakeUpWordEnabled = false,
4 activeLocales = emptyList(),
5 enabledEarcons = EnumSet.noneOf(VpaEarconType::class.java),
6 doNotDisturbModeEnabled = false,
7 locationSharingEnabled = false,
8 syncVehicleNavigationFavoritesEnabled = false,
9 allowedPersonalData = emptyMap()
10 )
11}

Push-To-Talk

To define how the VPA reacts to a push-to-talk button press, override the startListening() function. In this function you would typically inform the VPA engine that the user is ready to talk to the VPA.

src/main/kotlin/ExampleVpaService.kt

1override suspend fun startListening() {
2 // TODO: Handle push-to-talk activation.
3}

Privacy Mode

The privacy mode is controlled by the enablePrivacyMode() function.

src/main/kotlin/ExampleVpaService.kt

1override suspend fun enablePrivacyMode(enable: Boolean) {
2 // TODO: Enable or disable the privacy mode.
3}

Please refer to the VpaAdaptationService API reference documentation for detailed information on its properties and functions.

Create a service host

Your module will also need to define a service host where the service will be running, as well as provide a service host builder.

  • An ExampleVpaServiceHost class:
    src/main/kotlin/ExampleVpaServiceHost.kt

    1import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviDiscoverableServiceIdProvider
    2import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviServiceHostBase
    3import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviServiceHostContext
    4
    5/**
    6 * An [ExampleVpaServiceHost] host server.
    7 */
    8internal class ExampleVpaServiceHost(
    9 iviServiceHostContext: IviServiceHostContext,
    10 iviDiscoverableServiceIdProvider: IviDiscoverableServiceIdProvider
    11) :
    12 IviServiceHostBase(iviServiceHostContext) {
    13
    14 override val iviServices = setOf(
    15 ExampleVpaService(iviServiceHostContext, iviDiscoverableServiceIdProvider)
    16 )
    17}
  • An ExampleVpaServiceHostBuilder class:
    src/main/kotlin/ExampleVpaServiceHostBuilder.kt

    1/**
    2 * An [ExampleVpaServiceHost] builder used to build an [ExampleVpaService] host.
    3 */
    4class ExampleVpaServiceHostBuilder : IviServiceHostBuilder() {
    5
    6 override fun build(iviServiceHostContext: IviServiceHostContext): IviServiceHostBase =
    7 ExampleVpaServiceHostBuilder(iviServiceHostContext) {
    8 getDiscoverableServiceId(it)
    9 }
    10
    11 companion object
    12}

Please ensure that the ExampleVpaServiceHostBuilder class is added to the root of your module hierarchy.

Configure the service host deployment

Define an IVI service host implementation in your Gradle file, This can also be defined in a top-level gradle file (for example, iviservicehosts.gradle.kts) so it can be used in a multi-project build, including the tests.

/examples/vpa/iviservicehosts.gradle.kts

1import com.tomtom.ivi.buildsrc.dependencies.ExampleModuleReference
2import com.tomtom.ivi.platform.gradle.api.common.dependencies.IviPlatformModuleReference
3import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.IviServiceHostConfig
4import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.IviServiceInterfaceConfig
5
6/**
7 * Defines a configuration for the Example VPA service.
8 *
9 * The configuration specifies the service host implementation and the list of interfaces
10 * implemented by this service host.
11 */
12val exampleVpaServiceHost by extra {
13 IviServiceHostConfig(
14 serviceHostBuilderName = "ExampleVpaServiceHostBuilder",
15 implementationModule = ExampleModuleReference("examples_vpa_service"),
16 interfaces = listOf(
17 IviServiceInterfaceConfig(
18 serviceName = "VpaAdaptationService",
19 serviceId = "com.example.ivi.example.vpa",
20 serviceApiModule = IviPlatformModuleReference(
21 "platform_vpa_api_service_vpaadaptation"
22 )
23 )
24 )
25 )
26}

Register the service host build configuration in the main application's build script.

examples/vpa/app/build.gradle.kts

1import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.IviServiceHostConfig
2import com.tomtom.ivi.platform.gradle.api.framework.config.ivi
3
4// Define the service host configs as defined in the top-level `iviservicehosts.gradle.kts` file.
5apply(from = rootProject.file("examples/vpa/iviservicehosts.gradle.kts"))
6
7// Use Gradle's extra extensions to obtain the `exampleVpaServiceHost` config as defined in
8// the top-level `iviservicehosts.gradle.kts` file.
9val exampleVpaServiceHost: IviServiceHostConfig by project.extra
10
11ivi {
12 application {
13 enabled = true
14 services {
15 // Add the Example VPA service host to the application.
16 addHost(exampleVpaServiceHost)
17 }
18 }
19}
20
21// The rest of the build script, dependencies, etc.