Expand Minimize Picture-in-picture Power Device Status Voice Recognition Skip Back Skip Forward Minus Plus Play Search
Internet Explorer alert
This browser is not recommended for use with smartdevicelink.com, and may not function properly. Upgrade to a different browser to guarantee support of all features.
close alert
To Top Created with Sketch. To Top
To Bottom Created with Sketch. To Bottom
JavaScript Suite Guides
Creating an App Service

Creating an App Service (RPC v5.1+)

App services is a powerful feature enabling both a new kind of vehicle-to-app communication and app-to-app communication via SDL.

App services are used to publish navigation, weather and media data (such as temperature, navigation waypoints, or the current playlist name). This data can then be used by both the vehicle head unit and, if the publisher of the app service desires, other SDL apps.

Vehicle head units may use these services in various ways. One app service for each type will be the "active" service to the module. For media, for example, this will be the media app that the user is currently using or listening to. For navigation, it would be a navigation app that the user is using to navigate. For weather, it may be the last used weather app, or a user-selected default. The system may then use that service's data to perform various actions (such as navigating to an address with the active service or to display the temperature as provided from the active weather service).

An SDL app can also subscribe to a published app service. Once subscribed, the app will be sent the new data when the app service publisher updates its data. To find out more about how to subscribe to an app service check out the Using App Services guide. Subscribed apps can also send certain RPCs and generic URI-based actions (see the section Supporting Service RPCs and Actions below) to your service.

Currently, there is no high-level API support for publishing an app service, so you will have to use raw RPCs for all app service related APIs.

Using an app service is covered in another guide.

App Service Types

Apps are able to declare that they provide an app service by publishing an app service manifest. Three types of app services are currently available and more will be made available over time. The currently available types are: Media, Navigation, and Weather. An app may publish multiple services (one for each of the different service types) if desired.

Publishing an App Service

Publishing a service is a multi-step process. First, you need to create your app service manifest. Second, you will publish your app service to the module. Third, you will publish the service data using OnAppServiceData. Fourth, you must listen for data requests and respond accordingly. Fifth, if your app service supports handling of RPCs related to your service you must listen for these RPC requests and handle them accordingly. Sixth, optionally, you can support URI-based app actions. Finally, if necessary, you can you update or delete your app service manifest.

1. Creating an App Service Manifest

The first step to publishing an app service is to create an AppServiceManifest object. There is a set of generic parameters you will need to fill out as well as service type specific parameters based on the app service type you are creating.

const manifest = new SDL.rpc.messages.AppServiceManifest()
    .setServiceType(SDL.rpc.enums.AppServiceType.MEDIA)
    .setServiceName('My Media App') // Must be unique across app services.
    .setServiceIcon(new SDL.rpc.structs.Image()
        .setValueParam('Service Icon Name')
        .setImageType(SDL.rpc.enums.ImageType.DYNAMIC)) // Previously uploaded service icon. This could be the same as your app icon.
    .setAllowAppConsumers(true) // Whether or not other apps can view your data in addition to the head unit. If set to `false` only the head unit will have access to this data.
    .setRpcSpecVersion(new SDL.rpc.structs.SdlMsgVersion()
        .setMajorVersion(5)
        .setMinorVersion(0)) // An *optional* parameter that limits the RPC spec versions you can understand to the provided version *or below*.
    .setHandledRpcs([]) // If you add function ids to this *optional* parameter, you can support newer RPCs on older head units (that don't support those RPCs natively) when those RPCs are sent from other connected applications.
    .setMediaServiceManifest(mediaManifest); // Covered Below

Creating a Media Service Manifest

Currently, there's no information you have to provide in your media service manifest! You'll just have to create an empty media service manifest and set it into your general app service manifest.

const mediaManifest = new SDL.rpc.structs.MediaServiceManifest()
manifest.setMediaServiceManifest(mediaManifest);

Creating a Navigation Service Manifest

You will need to create a navigation manifest if you want to publish a navigation service. You will declare whether or not your navigation app will accept waypoints. That is, if your app will support receiving multiple points of navigation (e.g. go to this McDonalds, then this Walmart, then home).

const navigationManifest = new SDL.rpc.structs.NavigationServiceManifest()
    .setAcceptsWayPoints(true);
manifest.setNavigationServiceManifest(navigationManifest);

Creating a Weather Service Manifest

You will need to create a weather service manifest if you want to publish a weather service. You will declare the types of data your service provides in its WeatherServiceData.

const weatherManifest = new SDL.rpc.structs.WeatherServiceManifest()
    .setCurrentForecastSupported(true)
    .setMaxMultidayForecastAmount(10)
    .setMaxHourlyForecastAmount(24)
    .setMaxMinutelyForecastAmount(60)
    .setWeatherForLocationSupported(true);
manifest.setWeatherServiceManifest(weatherManifest);

2. Publish Your Service

Once you have created your service manifest, publishing your app service is simple.

// sdl_javascript_suite v1.1+
const publishServiceRequest = new SDL.rpc.messages.PublishAppService()
    .setAppServiceManifest(manifest);
const response = await sdlManager.sendRpcResolve(publishServiceRequest);
// thrown exceptions should be caught by a parent function via .catch()

// Pre sdl_javascript_suite v1.1
const publishServiceRequest = new SDL.rpc.messages.PublishAppService()
    .setAppServiceManifest(manifest);
const response = await sdlManager.sendRpc(publishServiceRequest)
    .catch(error => error);

Once you have your publish app service response, you will need to store the information provided in its appServiceRecord property. You will need the information later when you want to update your service's data.

Watching for App Record Updates

As noted in the introduction to this guide, one service for each type may become the "active" service. If your service is the active service, your AppServiceRecord parameter serviceActive will be updated to note that you are now the active service.

After the initial app record is passed to you in the PublishAppServiceResponse, you will need to be notified of changes in order to observe whether or not you have become the active service. To do so, you will have to observe the new SystemCapabilityType.APP_SERVICES using GetSystemCapability and OnSystemCapabilityUpdated.

For more information, see the Using App Services guide and go to the Getting and Subscribing to Services section.

3. Update Your Service's Data

After your service is published, it's time to update your service data. First, you must send an onAppServiceData RPC notification with your updated service data. RPC notifications are different than RPC requests in that they will not receive a response from the connected head unit .

Note

You should only update your service's data when you are the active service; service consumers will only be able to see your data when you are the active service.

First, you will have to create an MediaServiceData, NavigationServiceData or
WeatherServiceData object with your service's data. Then, add that service-specific data object to an AppServiceData object. Finally, create an OnAppServiceData notification, append your AppServiceData object, and send it.

Media Service Data

const mediaData = new SDL.rpc.structs.MediaServiceData();
    .setMediaTitle('Some media title')
    .setMediaArtist('Some media artist')
    .setMediaAlbum('Some album')
    .setMediaImage(new SDL.rpc.structs.Image()
        .setValueParam('Some image')
        .setImageType(SDL.rpc.enums.ImageType.DYNAMIC))
    .setPlaylistName('Some playlist')
    .setIsExplicit(true)
    .setTrackPlaybackProgress(45)
    .setTrackPlaybackDuration(90)
    .setQueuePlaybackProgress(45)
    .setQueuePlaybackDuration(150)
    .setQueueCurrentTrackNumber(2)
    .setQueueTotalTrackCount(3);

const appData = new SDL.rpc.structs.AppServiceData()
    .setServiceID(myServiceId)
    .setServiceType(SDL.rpc.enums.AppServiceType.MEDIA)
    .setMediaServiceData(mediaData);

const onAppData = new SDL.rpc.messages.OnAppServiceData()
    .setServiceData(appData);

// sdl_javascript_suite v1.1+
sdlManager.sendRpcResolve(onAppData);
// Pre sdl_javascript_suite v1.1
sdlManager.sendRpc(onAppData);
const navInstructionArt = SDL.manager.file.filetypes.SdlArtwork('turn', SDL.rpc.enums.FileType.GRAPHIC_PNG, image, true);
// We have to send the image to the system before it's used in the app service.
const success = await sdlManager.getFileManager().uploadFile(navInstructionArt);
if (success) {
    const coordinate = new SDL.rpc.structs.Coordinate()
        .setLatitudeDegrees(42)
        .setLongitudeDegrees(43);

    const locationDetails = new SDL.rpc.structs.LocationDetails()
        .setCoordinate(coordinate);

    const navigationInstruction = new SDL.rpc.structs.NavigationInstruction()
        .setLocationDetails(locationDetails)
        .setAction(SDL.rpc.enums.NavigationAction.TURN)
        .setImage(navInstructionArt.getImageRPC());

    const dateTime = new SDL.rpc.structs.DateTime()
        .setHour(2)
        .setMinute(3)
        .setSecond(4);

    const navigationData = new SDL.rpc.structs.NavigationServiceData()
        .setTimeStamp(dateTime)
        .setInstructions([navigationInstruction]);

    const appData = new SDL.rpc.structs.AppServiceData()
        .setServiceID(myServiceId)
        .setServiceType(SDL.rpc.enums.AppServiceType.NAVIGATION)
        .setNavigationServiceData(navigationData);

    const onAppData = new SDL.rpc.messages.OnAppServiceData()
        .setServiceData(appData);

    // sdl_javascript_suite v1.1+
    sdlManager.sendRpcResolve(onAppData);
    // Pre sdl_javascript_suite v1.1
    sdlManager.sendRpc(onAppData);
}

Weather Service Data

const weatherImage = SDL.manager.file.filetypes.SdlArtwork('sun', SDL.rpc.enums.FileType.GRAPHIC_PNG, image, true);
// We have to send the image to the system before it's used in the app service.
const success = await sdlManager.getFileManager().uploadFile(weatherImage);
if (success) {
    const weatherData = new SDL.rpc.structs.WeatherData()
        .setWeatherIcon(weatherImage.getImageRPC());

    const coordinate = new SDL.rpc.structs.Coordinate()
        .setLatitudeDegrees(42)
        .setLongitudeDegrees(43);

    const locationDetails = new SDL.rpc.structs.LocationDetails()
        .setCoordinate(coordinate);

    const weatherServiceData = new SDL.rpc.structs.WeatherServiceData()
        .setLocation(locationDetails);

    const appData = new SDL.rpc.structs.AppServiceData()
        .setServiceID(myServiceId)
        .setServiceType(SDL.rpc.enums.AppServiceType.WEATHER)
        .setWeatherServiceData(weatherServiceData);

    const onAppData = new SDL.rpc.messages.OnAppServiceData()
        .setServiceData(appData);

    // sdl_javascript_suite v1.1+
    sdlManager.sendRpcResolve(onAppData);
    // Pre sdl_javascript_suite v1.1
    sdlManager.sendRpc(onAppData);
}

4. Handling App Service Subscribers

If you choose to make your app service available to other apps, you will have to handle requests to get your app service data when a consumer requests it directly.

Handling app service subscribers is a two step process. First, you must setup listeners for the subscriber. Then, when you get a request, you will either have to send a response to the subscriber with the app service data or if you have no data to send, send a response with a relevant failure result code.

Listening for Requests

First, you will need to setup a listener for GetAppServiceDataRequest. Then, when you get the request, you will need to respond with your app service data. Therefore, you will need to store your current service data after the most recent update using OnAppServiceData (see the section Update Your Service's Data).

// Get App Service Data Request Listener
sdlManager.addRpcListener(SDL.rpc.enums.FunctionID.GetAppServiceData, (message) => {
    if (message.getMessageType() === SDL.rpc.enums.MessageType.request) {
        const getAppServiceData = message;

        const response = new SDL.rpc.messages.GetAppServiceDataResponse()
            .setSuccess(true)
            .setCorrelationID(getAppServiceData.getCorrelationId())
            .setResultCode(SDL.rpc.enums.Result.SUCCESS)
            .setInfo('Use to provide more information about an error')
            .setServiceData(appServiceData);

        // sdl_javascript_suite v1.1+
        sdlManager.sendRpcResolve(response);
        // Pre sdl_javascript_suite v1.1
        sdlManager.sendRpc(response);
    }
});

Supporting Service RPCs and Actions

5. Service RPCs

Certain RPCs are related to certain services. The chart below shows the current relationships:

MEDIA NAVIGATION WEATHER
ButtonPress (OK) SendLocation
ButtonPress (SEEKLEFT) GetWayPoints
ButtonPress (SEEKRIGHT) SubscribeWayPoints
ButtonPress (TUNEUP) OnWayPointChange
ButtonPress (TUNEDOWN)
ButtonPress (SHUFFLE)
ButtonPress (REPEAT)

When you are the active service for your service's type (e.g. media), and you have declared that you support these RPCs in your manifest (see the section Creating an App Service Manifest), then these RPCs will be automatically routed to your app. You will have to set up listeners to be aware that they have arrived, and you will then need to respond to those requests.

const manifest = new SDL.rpc.structs.AppServiceManifest(SDL.rpc.enums.AppServiceType.MEDIA);
...
manifest.setHandledRpcs([SDL.rpc.enums.FunctionID.ButtonPress]);
sdlManager.addRpcListener(SDL.rpc.enums.FunctionID.ButtonPress, (message) => {
    if (message.getMessageType() === SDL.rpc.enums.MessageType.request) {
        const buttonPress = message;

        const response = new SDL.rpc.messages.ButtonPressResponse()
            .setSuccess(true)
            .setResultCode(SDL.rpc.enums.Result.SUCCESS)
            .setCorrelationID(buttonPress.getCorrelationId())
            .setInfo('Use to provide more information about an error');

        // sdl_javascript_suite v1.1+
        sdlManager.sendRpcResolve(response);
        // Pre sdl_javascript_suite v1.1
        sdlManager.sendRpc(response);
    }
});

6. Service Actions

App actions are the ability for app consumers to use the SDL services system to send URIs to app providers in order to activate actions on the provider. Service actions are schema-less, i.e. there is no way to define the appropriate URIs through SDL. If you already provide actions through your app and want to expose them to SDL, or if you wish to start providing them, you will have to document your available actions elsewhere (such as your website).

In order to support actions through SDL services, you will need to observe and respond to the PerformAppServiceInteraction RPC request.

// Perform App Services Interaction Request Listener
sdlManager.addRpcListener(SDL.rpc.enums.FunctionID.PerformAppServiceInteraction, (message) => {
    if (message.getMessageType() === SDL.rpc.enums.MessageType.request) {
        const performAppServiceInteraction = message;

        // If you have multiple services, this will let you know which of your services is being addressed
        const serviceId = performAppServiceInteraction.getServiceID();

        // A result you want to send to the consumer app.
        const response = new SDL.rpc.messages.PerformAppServiceInteractionResponse()
            .setServiceSpecificResult('Some Result')
            .setCorrelationID(performAppServiceInteraction.getCorrelationId())
            .setInfo('Use to provide more information about an error')
            .setSuccess(true)
            .setResultCode(SDL.rpc.enums.Result.SUCCESS);

        // sdl_javascript_suite v1.1+
        sdlManager.sendRpcResolve(response);
        // Pre sdl_javascript_suite v1.1
        sdlManager.sendRpc(response);
    }
});

Updating Your Published App Service

Once you have published your app service, you may decide to update its data. For example, if you have a free and paid tier with different amounts of data, you may need to upgrade or downgrade a user between these tiers and provide new data in your app service manifest. If desired, you can also delete your app service by unpublishing the service.

7. Updating a Published App Service Manifest (RPC v6.0+)

const manifest = new SDL.rpc.structs.AppServiceManifest()
    .getServiceType(SDL.rpc.enums.AppServiceType.WEATHER)
    .setWeatherServiceManifest(weatherServiceManifest);

const publishServiceRequest = new SDL.rpc.messages.PublishAppService()
    .setAppServiceManifest(manifest);

// sdl_javascript_suite v1.1+
sdlManager.sendRpcResolve(publishServiceRequest);
// Pre sdl_javascript_suite v1.1
sdlManager.sendRpc(publishServiceRequest);

8. Unpublishing a Published App Service Manifest (RPC v6.0+)

const unpublishAppService = new SDL.rpc.messages.UnpublishAppService()
    .setServiceID(serviceId);

// sdl_javascript_suite v1.1+
sdlManager.sendRpcResolve(unpublishAppService);
// Pre sdl_javascript_suite v1.1
sdlManager.sendRpc(unpublishAppService);
View on GitHub.com
Previous Section Next Section