Configure the Runtime Deployment of the IVI System

Android allows the running of Android services in processes separate from the main application/activity process(es). The TomTom Digital Cockpit platform uses Android services for IVI service hosts and therefore it is possible to isolate the IVI service host instances too, see IVI Services for more details about IVI service hosts. The main advantage of process isolation is that when a process unexpectedly crashes (and restarts), this only affects the service host instances running in that process and not all other services and does not affect the UI.

The default IVI application configuration deploys each service host implementation in a separate process. It is also possible to run multiple IVI service hosts in one process, to limit the impact of the Binder IPC and reduce the (limited) overhead of each process. The IVI services provided by TomTom can be rearranged, default implementations can be replaced by new ones and services can be removed from the deployment.

An IVI service host can be deployed to multiple runtime deployments. This allows multiple instances of the service host to run in separate processes.

IVI instance overview

A vehicle may have multiple infotainment screens. Each infotainment screen is an IVI instance. There is at least a single default IVI instance, typically the one associated with the center stack. A single system may run multiple IVI instances that all need access to service hosts which are specific to each IVI instance and service hosts that are accessible by all IVI instances.

Runtime deployment configuration overview

There are two aspects of an IVI runtime configuration:

globalDeployments

The RuntimeConfigurator.globalDeployments() will contain the service hosts that host IVI Service APIs that are relevant for the whole application. These service hosts will only have one instance available system-wide.

In production, there should be only one runtime deployment created inside the globalDeployments configuration: the RuntimeDeploymentIdentifier.globalRuntime. However, it is possible to define multiple runtimes. This can be useful, for instance when creating integration tests, in order to test how certain functionality behaves under different deployment configurations. The example below shows two global deployment configurations for the connectionTestServiceHosts. The OwnProcessDeployment creates an instance of connectionTestServiceHosts which runs in its own process, and MainProcessDeployment creates an instance in the main process. This deployment could be useful to test if a workflow behaves properly when deployed in its own process or just the main one.

build.gradle.kts:

1import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.IviServiceHostConfig
2import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.RuntimeDeploymentIdentifier
3import com.tomtom.ivi.platform.gradle.api.framework.config.ivi
4
5val connectionTestServiceHosts = IviServiceHostConfig(...)
6
7ivi {
8 application {
9 enabled = true
10 ...
11 runtime {
12 globalDeployments {
13 create(RuntimeDeploymentIdentifier.globalRuntime)
14 create("OwnProcessDeployment") {
15 autoRegister = false
16 deployServiceHosts(connectionTestServiceHosts).asBinderHost()
17 }
18 create("MainProcessDeployment") {
19 autoRegister = false
20 deployServiceHosts(connectionTestServiceHosts).asBinderHost()
21 .inMainProcess()
22 }
23 }
24 }
25 }
26}

multipleInstanceDeployments

The RuntimeConfigurator.multipleInstanceDeployments() will contain service hosts that are relevant for a given IVI instance. Multiple IVI instances can be configured in one deployment. This means, that a given process will run as many instances of a service host as there are IVI instances configured.

In production, there can be multiple runtimes created inside the multipleInstanceDeployments configuration. This can be useful to increase the reliability of the system, since it will enable the isolation of service hosts. In the example below there will be one process spawned with an instance of accountServiceHost associated with the passengerIviInstance, and another process with an instance of accountServiceHost associated with the accountServiceHost. Therefore, if one of them crashes there will be no impact on the other.

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
4val accountServiceHost = IviServiceHostConfig(...)
5
6ivi {
7 application {
8 enabled = true
9 ...
10 runtime {
11 multipleInstanceDeployments {
12 create("DriverDeployment") {
13 iviInstances = listOf(driverIviInstance)
14 deployServiceHost(accountServiceHost)
15 }
16 create("PassengerDeployment") {
17 iviInstances = listOf(passengerIviInstance)
18 deployServiceHost(accountServiceHost)
19 }
20 }
21 }
22 }
23}

How to extend the default runtime deployment

In the main application build script, you can override the default runtime deployment. For example:

build.gradle.kts:

1import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.IviServiceHostConfig
2import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.RuntimeDeploymentIdentifier
3import com.tomtom.ivi.platform.gradle.api.framework.config.ivi
4
5apply(from = rootProject.file("iviservicehosts.gradle.kts"))
6
7val accountsServiceHosts: List<IviServiceHostConfig> by project.extra
8
9ivi {
10 application {
11 enabled = true
12 ...
13 runtime {
14 globalDeployments {
15 // Create the "Global" runtime deployment.
16 create(RuntimeDeploymentIdentifier.globalRuntime) {
17 // Apply the default runtime deployments. This deploys each IVI service host
18 // implementation in a separate process.
19 applyDefaultDeployments(all())
20
21 // Deploy the `accountsServiceHosts` in the same process.
22 deployServiceHosts(inList(accountsServiceHosts))
23 .withProcessName("account")
24 }
25 }
26 }
27 }
28}

The above example uses the default deployment configuration and configures the accountsServiceHosts to run in the same process.

How to replace a default service host

The default IVI service hosts can be found inside the module com.tomtom.ivi.platform.gradle.api.defaults.config and in the api_appsuitedefaults_* modules. It is possible to replace a default service host, for instance to deploy a new implementation of an IVI service API. The example below replaces the default contactsServiceHost with a custom cloudContactsServiceHost.

build.gradle.kts:

1import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.IviServiceHostConfig
2import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.RuntimeDeploymentIdentifier
3import com.tomtom.ivi.platform.gradle.api.defaults.config.contactsServiceHost
4import com.tomtom.ivi.platform.gradle.api.framework.config.ivi
5
6val cloudContactsServiceHost = IviServiceHostConfig(...)
7
8ivi {
9 application {
10 enabled = true
11 services {
12 removeHost(contactsServiceHost)
13 addHost(cloudContactsServiceHost)
14 }
15 globalRuntime {
16 create(RuntimeDeploymentIdentifier.global) {
17 applyDefaultDeployments(all())
18 deployServiceHost(cloudContactsServiceHost)
19 }
20 }
21 }
22}

How to add an IVI instance

A vehicle may have multiple infotainment screens. Each infotainment screen is an IVI instance.

To add an IVI instance, it needs to be created in the main application build script, and needs to be mapped to a runtime deployment. The following example defines two IVI instances, the "CenterStack" instance and the "Passenger" instance, and maps each IVI instance to its own runtime deployment.

build.gradle.kts:

1import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.IviInstanceIdentifier
2import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.RuntimeDeploymentIdentifier
3import com.tomtom.ivi.platform.gradle.api.defaults.config.mainMenuFrontend
4import com.tomtom.ivi.platform.gradle.api.framework.config.ivi
5
6val centerStackIviInstance = IviInstanceIdentifier("CenterStack")
7val passengerIviInstance = IviInstanceIdentifier("Passenger")
8
9ivi {
10 application {
11 enabled = true
12 iviInstances {
13 // Create the "CenterStack" IVI instance with the default frontends and menu items.
14 create(centerStackIviInstance) {
15 applyGroups { includeDefaultGroups() }
16 }
17 // Create the "Passenger" IVI instance. In this example only the `mainMenuFrontend` is
18 // added.
19 create(passengerIviInstance) {
20 frontends {
21 add(mainMenuFrontend, ...)
22 }
23 }
24 }
25 runtime {
26 globalDeployments {
27 // Create "Global" runtime deployment to deploy all global IVI service hosts.
28 create(RuntimeDeploymentIdentifier.globalRuntime) {
29 deployServiceHosts(all())
30 }
31 }
32 multipleInstanceDeployments {
33 // Create "CenterStack" runtime deployment to deploy all service hosts for the
34 // "CenterStack" IVI instances.
35 create(RuntimeDeploymentIdentifier("CenterStackRuntime")) {
36 iviInstances = listOf(centerStackIviInstance)
37 deployServiceHosts(all())
38 }
39 // Create "Passenger" runtime deployment to deploy all service hosts for the
40 // "Passenger" IVI instances.
41 create(RuntimeDeploymentIdentifier("PassengerRuntime")) {
42 iviInstances = listOf(passengerIviInstance)
43 deployServiceHosts(all())
44 }
45 }
46 }
47 }
48}

The above configuration results in running all IVI service host instances in their own process.

Note: It is also possible to map multiple IVI instances to the same runtime deployment. In this case the IVI service host instances of these IVI instances will run in the same process. Another option is to selectively deploy services across deployments.

To use an IVI instance, an Android Activity needs to be bound to an IVI instance. The Android manifest entry for the activity must define a metadata entry with the name com.tomtom.ivi.platform.framework.api.product.activity.IVI_INSTANCE and the value of the name of IVI instance. The activity must subclass the IviActivity class. To use the default system UI use DefaultActivity as the base class.

AndroidManifest.xml:

1<activity
2 android:name=".PassengerActivity"
3 android:label="@string/ttivi_passenger_activity_label"
4 android:launchMode="singleTask"
5 android:windowSoftInputMode="adjustPan">
6
7 <intent-filter>
8 <action android:name="com.example.mydigitalcockpitapp.PASSENGER" />
9 <category android:name="android.intent.category.DEFAULT" />
10 </intent-filter>
11
12 <meta-data
13 android:name="com.tomtom.ivi.platform.framework.api.product.activity.IVI_INSTANCE"
14 android:value="Passenger" />
15</activity>

The above example defines a PassengerActivity that is associate to the Passenger IVI Instance.

How to run an Android service in the same process as an IVI service host

Standard Android services (not IVI service hosts) are not managed by the IVI platform in any way. The IVI build config only allows an Android service to be deployed in a configurable process name. For instance, it is possible to deploy an Android service in the same process as an IVI service host without the need to hard-code the process name of the Android service in an AndroidManifest.xml file.

build.gradle.kts:

1import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.AndroidServiceConfig
2import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.IviServiceHostConfig
3import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.RuntimeDeploymentIdentifier
4import com.tomtom.ivi.platform.gradle.api.framework.config.ivi
5
6val androidService = AndroidServiceConfig("com....Service")
7val someServiceHost = IviServiceHostConfig(...)
8
9ivi {
10 application {
11 enabled = true
12 services {
13 addHost(someServiceHost)
14 }
15 globalRuntime {
16 create(RuntimeDeploymentIdentifier.global) {
17 applyDefaultDeployments(all())
18 deployServiceHost(someServiceHost)
19 deployAndroidService(androidService).inSameProcessAs(someServiceHost)
20 }
21 }
22 }
23}

The above example deploys com....Service in the same process as someServiceHost.

Note: The IVI build config does not manage the Gradle dependencies to include the referenced Android service into the build.

How to run a broadcast receiver in the same process as an IVI service host

Android broadcast receivers are not managed by the IVI platform in any way. The IVI build config only allows a broadcast receiver to be deployed in a configurable process name. For instance, it is possible to deploy a broadcast receiver in the same process as an IVI service host without the need to hard-code the process name of the broadcast receiver in an AndroidManifest.xml file.

build.gradle.kts:

1import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.BroadcastReceiverConfig
2import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.IviServiceHostConfig
3import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.RuntimeDeploymentIdentifier
4import com.tomtom.ivi.platform.gradle.api.framework.config.ivi
5
6val broadcastReceiver = BroadcastReceiverConfig("com....BroadcastReceiver")
7val someServiceHost = IviServiceHostConfig(...)
8
9ivi {
10 application {
11 enabled = true
12 services {
13 addHost(someServiceHost)
14 }
15 globalRuntime {
16 create(RuntimeDeploymentIdentifier.global) {
17 applyDefaultDeployments(all())
18 deployServiceHost(someServiceHost)
19 deployBroadcastReceiver(broadcastReceiver).inSameProcessAs(someServiceHost)
20 }
21 }
22 }
23}

The above example deploys com....BroadcastReceiver in the same process as someServiceHost.

Note: The IVI build config does not manage the Gradle dependencies to include the referenced broadcast receiver into the build.