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.
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 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.
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.
AppServiceManifest manifest = new AppServiceManifest(AppServiceType.MEDIA.toString()) .setServiceName("My Media App") // Must be unique across app services. .setServiceIcon(new Image("Service Icon Name", 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 SdlMsgVersion(5,0)) // An *optional* parameter that limits the RPC spec versions you can understand to the provided version *or below*. .setHandledRpcs(List<FunctionID>) // 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
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.
MediaServiceManifest mediaManifest = new MediaServiceManifest(); manifest.setMediaServiceManifest(mediaManifest);
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).
NavigationServiceManifest navigationManifest = new NavigationServiceManifest(); navigationManifest.setAcceptsWayPoints(true); manifest.setNavigationServiceManifest(navigationManifest);
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
.
WeatherServiceManifest weatherManifest = new WeatherServiceManifest() .setCurrentForecastSupported(true) .setMaxMultidayForecastAmount(10) .setMaxHourlyForecastAmount(24) .setMaxMinutelyForecastAmount(60) .setWeatherForLocationSupported(true); manifest.setWeatherServiceManifest(weatherManifest);
Once you have created your service manifest, publishing your app service is simple.
PublishAppService publishServiceRequest = new PublishAppService() .setAppServiceManifest(manifest); publishServiceRequest.setOnRPCResponseListener(new OnRPCResponseListener() { @Override public void onResponse(int correlationId, RPCResponse response) { if (response.getSuccess()) { // Use the response } else { // Error Handling } } }); sdlManager.sendRPC(publishServiceRequest);
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.
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.
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 .
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.
MediaServiceData mediaData = new MediaServiceData() .setMediaTitle("Some media title") .setMediaArtist("Some media artist") .setMediaAlbum("Some album") .setMediaImage(new Image("Some image", ImageType.DYNAMIC)) .setPlaylistName("Some playlist") .setIsExplicit(true) .setTrackPlaybackProgress(45) .setTrackPlaybackDuration(90) .setQueuePlaybackProgress(45) .setQueuePlaybackDuration(150) .setQueueCurrentTrackNumber(2) .setQueueTotalTrackCount(3); AppServiceData appData = new AppServiceData() .setServiceID(myServiceId) .setServiceType(AppServiceType.MEDIA.toString()) .setMediaServiceData(mediaData); OnAppServiceData onAppData = new OnAppServiceData(); onAppData.setServiceData(appData); sdlManager.sendRPC(onAppData);
final SdlArtwork navInstructionArt = new SdlArtwork("turn", FileType.GRAPHIC_PNG, image, true); sdlManager.getFileManager().uploadFile(navInstructionArt, new CompletionListener() { @Override public void onComplete(boolean success) { if (success){ Coordinate coordinate = new Coordinate(42f,43f); LocationDetails locationDetails = new LocationDetails(); locationDetails.setCoordinate(coordinate); // Make sure the image is uploaded to the system before publishing your data NavigationInstruction navigationInstruction = new NavigationInstruction(locationDetails, NavigationAction.TURN); navigationInstruction.setImage(navInstructionArt.getImageRPC()); DateTime dateTime = new DateTime() .setHour(2) .setMinute(3) .setSecond(4); NavigationServiceData navigationData = new NavigationServiceData(dateTime); navigationData.setInstructions(Collections.singletonList(navigationInstruction)); AppServiceData appData = new AppServiceData() .setServiceID(myServiceId) .setServiceType(AppServiceType.NAVIGATION.toString()) .setNavigationServiceData(navigationData); OnAppServiceData onAppData = new OnAppServiceData(); onAppData.setServiceData(appData); sdlManager.sendRPC(onAppData); } } });
final SdlArtwork weatherImage = new SdlArtwork("sun", FileType.GRAPHIC_PNG, image, true); sdlManager.getFileManager().uploadFile(weatherImage, new CompletionListener() { @Override public void onComplete(boolean success) { if (success) { // Make sure the image is uploaded to the system before publishing your data WeatherData weatherData = new WeatherData(); weatherData.setWeatherIcon(weatherImage.getImageRPC()); Coordinate coordinate = new Coordinate(42f, 43f); LocationDetails locationDetails = new LocationDetails(); locationDetails.setCoordinate(coordinate); WeatherServiceData weatherServiceData = new WeatherServiceData(locationDetails); AppServiceData appData = new AppServiceData() .setServiceID(myServiceId) .setServiceType(AppServiceType.WEATHER.toString()) .setWeatherServiceData(weatherServiceData); OnAppServiceData onAppData = new OnAppServiceData(); onAppData.setServiceData(appData); sdlManager.sendRPC(onAppData); } } });
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.
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).
sdlManager.addOnRPCRequestListener(FunctionID.GET_APP_SERVICE_DATA, new OnRPCRequestListener() { @Override public void onRequest(RPCRequest request) { GetAppServiceData getAppServiceData = (GetAppServiceData) request; // Send a response GetAppServiceDataResponse response = new GetAppServiceDataResponse(); response.setSuccess(true); response.setCorrelationID(getAppServiceData.getCorrelationID()); response.setResultCode(Result.SUCCESS); response.setInfo("<#Use to provide more information about an error#>"); response.setServiceData(appServiceData); sdlManager.sendRPC(response); } });
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.
AppServiceManifest manifest = new AppServiceManifest(AppServiceType.MEDIA.toString()); ... manifest.setHandledRpcs(Collections.singletonList(FunctionID.BUTTON_PRESS.getId()));
sdlManager.addOnRPCRequestListener(FunctionID.BUTTON_PRESS, new OnRPCRequestListener() { @Override public void onRequest(RPCRequest request) { ButtonPress buttonPress = (ButtonPress) request; ButtonPressResponse response = new ButtonPressResponse(); response.setSuccess(true); response.setResultCode(Result.SUCCESS); response.setCorrelationID(buttonPress.getCorrelationID()); response.setInfo("<#Use to provide more information about an error#>"); sdlManager.sendRPC(response); } });
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.addOnRPCRequestListener(FunctionID.PERFORM_APP_SERVICES_INTERACTION, new OnRPCRequestListener() { @Override public void onRequest(RPCRequest request) { PerformAppServiceInteraction performAppServiceInteraction = (PerformAppServiceInteraction) request; // If you have multiple services, this will let you know which of your services is being addressed serviceID = performAppServiceInteraction.getServiceID(); // The URI sent by the consumer. This must be something you understand String serviceURI = performAppServiceInteraction.getServiceUri(); // A result you want to send to the consumer app. PerformAppServiceInteractionResponse response = new PerformAppServiceInteractionResponse() .setServiceSpecificResult("Some Result"); response.setCorrelationID(performAppServiceInteraction.getCorrelationID()); response.setInfo("<#Use to provide more information about an error#>"); response.setSuccess(true); response.setResultCode(Result.SUCCESS); sdlManager.sendRPC(response); } });
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.
AppServiceManifest manifest = new AppServiceManifest(AppServiceType.WEATHER.toString()); manifest.setWeatherServiceManifest(weatherServiceManifest); PublishAppService publishServiceRequest = new PublishAppService(manifest); sdlManager.sendRPC(publishServiceRequest);
UnpublishAppService unpublishAppService = new UnpublishAppService("<#The serviceID of the service to unpublish>"); sdlManager.sendRPC(unpublishAppService);