In this guide, we exclusively use IntelliJ. We are going to set-up a bare-bones application so you get started using SDL.
The SDL Java library supports Java 7 and above.
A SmartDeviceLink Service should be created to manage the lifecycle of the SDL session. The SdlService
should build and start an instance of the SdlManager
which will automatically connect with a head unit when available. This SdlManager
will handle sending and receiving messages to and from SDL after it is connected.
Create a new service and name it appropriately, for this guide we are going to call it SdlService
.
public class SdlService { //... }
In order to correctly connect to an SDL enabled head unit developers need to implement methods for the proper creation and disposing of an SdlManager
in our SdlService
.
An instance of SdlManager cannot be reused after it is closed and properly disposed of. Instead, a new instance must be created. Only one instance of SdlManager should be in use at any given time.
SdlManagerListener
method: onSystemInfoReceived
auto generates in Android Studio to returns false. This will cause your app to not connect. You must change it to true or implement logic to check system info to see if you wish for your app to connect to that system.
public class SdlService { //The manager handles communication between the application and SDL private SdlManager sdlManager = null; public SdlService(BaseTransportConfig config){ buildSdlManager(config); } public void start() { if(sdlManager != null){ sdlManager.start(); } } public void stop() { if (sdlManager != null) { sdlManager.dispose(); sdlManager = null; } } //... private void buildSdlManager(BaseTransportConfig transport) { if (sdlManager == null) { // The app type to be used Vector<AppHMIType> appType = new Vector<>(); appType.add(AppHMIType.MEDIA); // The manager listener helps you know when certain events that pertain to the SDL Manager happen SdlManagerListener listener = new SdlManagerListener() { @Override public void onStart(SdlManager sdlManager) { // After this callback is triggered the SdlManager can be used to interact with the connected SDL session (updating the display, sending RPCs, etc) } @Override public void onDestroy(SdlManager sdlManager) { } @Override public void onError(SdlManager sdlManager, String info, Exception e) { } @Override public LifecycleConfigurationUpdate managerShouldUpdateLifecycle(Language language, Language hmiLanguage) { return null; } @Override public boolean onSystemInfoReceived(SystemInfo systemInfo) { // Check the SystemInfo object to ensure that the connection to the device should continue return true; } }; // Create App Icon, this is set in the SdlManager builder SdlArtwork appIcon = new SdlArtwork(ICON_FILENAME, FileType.GRAPHIC_PNG, ICON_PATH, true); // The manager builder sets options for your session SdlManager.Builder builder = new SdlManager.Builder(APP_ID, APP_NAME, listener); builder.setAppTypes(appType); builder.setTransportType(transport); builder.setAppIcon(appIcon); sdlManager = builder.build(); sdlManager.start(); } } }
The sdlManager
must be shutdown properly if this class is shutting down in the respective method using the method sdlManager.dispose()
.
This is a custom icon for your application. Please refer to Adaptive Interface Capabilities for icon sizes.
builder.setAppIcon(appIcon);
The app type is used by car manufacturers to decide how to categorize your app. Each car manufacturer has a different categorization system. For example, if you set your app type as media, your app will also show up in the audio tab as well as the apps tab of Ford’s SYNC® 3 head unit. The app type options are: default, communication, media (i.e. music/podcasts/radio), messaging, navigation, projection, information, and social.
Vector<AppHMIType> appHMITypes = new Vector<>(); appHMITypes.add(AppHMIType.MEDIA); builder.setAppTypes(appHMITypes);
This is a shortened version of your app name that is substituted when the full app name will not be visible due to character count constraints. You will want to make this as short as possible.
builder.setShortAppName(shortAppName);
You can customize the color scheme of your initial template on head units that support this feature using the builder
. For more information, see the Customizing the Template guide section.
Some OEMs may want to encrypt messages passed between your SDL app and the head unit. If this is the case, when you submit your app to the OEM for review, they will ask you to add a security library to your SDL app. See the Encryption section.
The file manager configuration allows you to configure retry behavior for uploading files and images. The default configuration attempts one re-upload, but will fail after that.
FileManagerConfig fileManagerConfig = new FileManagerConfig(); fileManagerConfig.setArtworkRetryCount(2); fileManagerConfig.setFileRetryCount(2); builder.setFileManagerConfig(fileManagerConfig);
The desired language to be used on display/HMI of connected module can be set.
builder.setLanguage(Language.EN_US);
You can listen for specific events using SdlManager
's builder setRPCNotificationListeners
. The following example shows how to listen for HMI Status notifications. Additional listeners can be added for specific RPCs by using their corresponding FunctionID
in place of the ON_HMI_STATUS
in the following example and casting the RPCNotification
object to the correct type.
Map<FunctionID, OnRPCNotificationListener> onRPCNotificationListenerMap = new HashMap<>(); onRPCNotificationListenerMap.put(FunctionID.ON_HMI_STATUS, new OnRPCNotificationListener() { @Override public void onNotified(RPCNotification notification) { OnHMIStatus onHMIStatus = (OnHMIStatus) notification; if (onHMIStatus.getHmiLevel() == HMILevel.HMI_FULL && onHMIStatus.getFirstRun()){ // first time in HMI Full } } }); builder.setRPCNotificationListeners(onRPCNotificationListenerMap);
You can also use addOnRPCNotificationListener
when creating an SdlManagerListener
object. The following example shows how to set up the listener in the onStart()
method of an SdlManagerListener
object.
@Override public void onStart() { // HMI Status Listener sdlManager.addOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, new OnRPCNotificationListener() { @Override public void onNotified(RPCNotification notification) { OnHMIStatus onHMIStatus = (OnHMIStatus) notification; if (onHMIStatus.getWindowID() != null && onHMIStatus.getWindowID() != PredefinedWindows.DEFAULT_WINDOW.getValue()) { return; } if (onHMIStatus.getHmiLevel() == HMILevel.HMI_FULL && onHMIStatus.getFirstRun()) { // first time in HMI Full } } }); }
Set a hashID
for your application that can be used over connection cycles (i.e. loss of connection, ignition cycles, etc.).
builder.setResumeHash(hashID);
You have the ability to determine a minimum SDL protocol and a minimum SDL RPC version that your app supports. You can also check the connected vehicle type and disconnect if the vehicle module is not supported. We recommend not setting these values until your app is ready for production. The OEMs you support will help you configure correct values during the application review process.
If a head unit is blocked by protocol version, your app icon will never appear on the head unit's screen. If you configure your app to block by RPC version, it will appear and then quickly disappear. So while blocking with minimumProtocolVersion
is preferable, minimumRPCVersion
allows you more granular control over which RPCs will be present.
builder.setMinimumProtocolVersion(new Version("3.0.0")); builder.setMinimumRPCVersion(new Version("4.0.0"));
If you are blocking by vehicle type and you are connected over RPC v7.1+, your app icon will never appear on the head unit's screen. If you are connected over RPC v7.0 or below, it will appear and then quickly disappear. To implement this type of blocking, you need to set up the SDLManagerListener
. You will then implement logic in onSystemInfoReceived
method and return true
if you want to continue the connection and false
if you wish to disconnect.
Create a new package where all the JavaEE-specific code will go.
The SDL Java library comes with a CustomTransport
class which takes the role of sending messages between incoming sdl_core connections and your SDL application. You need to pass that class to the SdlManager
builder to make the SDL Java library aware that you want to use your JavaEE websocket server as the transport.
Create a Java class in the new package which will be the SDLSessionBean
class. This class utilizes the CustomTransport
class and EJB JavaEE API which will make it the entry point of your app when a connection is made. It will open up a websocket server at /
and create stateful beans, where the bean represents the logic of your cloud app. Every new connection to this endpoint creates a new bean containing your app logic, allowing for load balancing across all the instances of your app that were automatically created.
import com.smartdevicelink.transport.CustomTransport; import javax.ejb.Stateful; import javax.websocket.*; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.nio.ByteBuffer; @ServerEndpoint("/") @Stateful(name = "SDLSessionEJB") public class SDLSessionBean { CustomTransport websocket; public class WebSocketEE extends CustomTransport { Session session; public WebSocketEE(String address, Session session) { super(address); this.session = session; } public void onWrite(byte[] bytes, int i, int i1) { try { session.getBasicRemote().sendBinary(ByteBuffer.wrap(bytes)); } catch (IOException e) { } } } @OnOpen public void onOpen (Session session) { websocket = new WebSocketEE("http://localhost", session) {}; //TODO: pass your CustomTransport instance to your SDL app here } @OnMessage public void onMessage (ByteBuffer message, Session session) { websocket.onByteBufferReceived(message); //received message from core } }
Unfortunately, there's no way to get a client's IP address using the standard API, so localhost is passed to the CustomTransport
for now as the transport address (this is only used locally in the library so it is not necessary).
The SDLSessionBean
class’s @OnOpen
method is where you will start your app, and should call your entry of your application and invoke whatever is needed to start it. You need to pass the instantiated CustomTransport
object to your application so that the connection can be passed into the SdlManager
.
The SdlManager
will need you to create a CustomTransportConfig
, pass in the CustomTransport
instance from the SDLSessionBean
instance, then set the SdlManager
Builder’s transport type to that config. This will set your transport type into CUSTOM
mode and will use your CustomTransport
instance to handle the read and write operations.
// Set transport config. builder is a SdlManager.Builder CustomTransportConfig transport = new CustomTransportConfig(websocket); builder.setTransportType(transport);
The SDLSessionBean
should be inside a Java package other than the default package in order for it to work properly.
You should now be able to connect to a head unit or emulator. For more guidance on connecting, see Connecting to an Infotainment System. To start building your app, learn about designing your interface. Please also review the best practices for building an SDL app.
View on GitHub.com