/*
* Copyright (c) 2019 Livio, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of the Livio Inc. nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package com.smartdevicelink.managers;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import com.smartdevicelink.exception.SdlException;
import com.smartdevicelink.managers.audio.AudioStreamManager;
import com.smartdevicelink.managers.file.FileManager;
import com.smartdevicelink.managers.file.FileManagerConfig;
import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
import com.smartdevicelink.managers.lifecycle.LifecycleConfigurationUpdate;
import com.smartdevicelink.managers.lockscreen.LockScreenConfig;
import com.smartdevicelink.managers.lockscreen.LockScreenManager;
import com.smartdevicelink.managers.permission.PermissionManager;
import com.smartdevicelink.managers.screen.ScreenManager;
import com.smartdevicelink.managers.video.VideoStreamManager;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.protocol.enums.SessionType;
import com.smartdevicelink.proxy.RPCMessage;
import com.smartdevicelink.proxy.RPCRequest;
import com.smartdevicelink.proxy.RPCResponse;
import com.smartdevicelink.proxy.SdlProxyBase;
import com.smartdevicelink.proxy.SystemCapabilityManager;
import com.smartdevicelink.proxy.callbacks.OnServiceEnded;
import com.smartdevicelink.proxy.callbacks.OnServiceNACKed;
import com.smartdevicelink.proxy.interfaces.IAudioStreamListener;
import com.smartdevicelink.proxy.interfaces.ISdl;
import com.smartdevicelink.proxy.interfaces.ISdlServiceListener;
import com.smartdevicelink.proxy.interfaces.IVideoStreamListener;
import com.smartdevicelink.proxy.interfaces.OnSystemCapabilityListener;
import com.smartdevicelink.proxy.rpc.ChangeRegistration;
import com.smartdevicelink.proxy.rpc.OnHMIStatus;
import com.smartdevicelink.proxy.rpc.RegisterAppInterfaceResponse;
import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
import com.smartdevicelink.proxy.rpc.SetAppIcon;
import com.smartdevicelink.proxy.rpc.TTSChunk;
import com.smartdevicelink.proxy.rpc.TemplateColorScheme;
import com.smartdevicelink.proxy.rpc.enums.AppHMIType;
import com.smartdevicelink.proxy.rpc.enums.Language;
import com.smartdevicelink.proxy.rpc.enums.Result;
import com.smartdevicelink.proxy.rpc.enums.SdlDisconnectedReason;
import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType;
import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCListener;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCRequestListener;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
import com.smartdevicelink.security.SdlSecurityBase;
import com.smartdevicelink.streaming.audio.AudioStreamingCodec;
import com.smartdevicelink.streaming.audio.AudioStreamingParams;
import com.smartdevicelink.streaming.video.VideoStreamingParameters;
import com.smartdevicelink.transport.BaseTransportConfig;
import com.smartdevicelink.transport.MultiplexTransportConfig;
import com.smartdevicelink.transport.enums.TransportType;
import com.smartdevicelink.transport.utl.TransportRecord;
import com.smartdevicelink.util.DebugTool;
import com.smartdevicelink.util.Version;
import org.json.JSONException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Vector;
/**
* SDLManager
*
* This is the main point of contact between an application and SDL
*
* It is broken down to these areas:
*
* 1. SDLManagerBuilder
* 2. ISdl Interface along with its overridden methods - This can be passed into attached managers
* 3. Sending Requests
* 4. Helper methods
*/
public class SdlManager extends BaseSdlManager{
private static final String TAG = "SdlManager";
private SdlProxyBase proxy;
private SdlArtwork appIcon;
private Context context;
private SdlManagerListener managerListener;
private List> sdlSecList;
private LockScreenConfig lockScreenConfig;
private FileManagerConfig fileManagerConfig;
private ServiceEncryptionListener serviceEncryptionListener;
// Managers
private PermissionManager permissionManager;
private FileManager fileManager;
private LockScreenManager lockScreenManager;
private ScreenManager screenManager;
private VideoStreamManager videoStreamManager;
private AudioStreamManager audioStreamManager;
// Initialize proxyBridge with anonymous lifecycleListener
private final ProxyBridge proxyBridge = new ProxyBridge(new ProxyBridge.LifecycleListener() {
@Override
public void onProxyConnected() {
DebugTool.logInfo("Proxy is connected. Now initializing.");
changeRegistrationRetry = 0;
checkLifecycleConfiguration();
initialize();
}
@Override
public void onProxyClosed(String info, Exception e, SdlDisconnectedReason reason){
if (!reason.equals(SdlDisconnectedReason.LANGUAGE_CHANGE)){
dispose();
}
}
@Override
public void onServiceEnded(OnServiceEnded serviceEnded){
}
@Override
public void onServiceNACKed(OnServiceNACKed serviceNACKed){
}
@Override
public void onError(String info, Exception e){
}
});
// Sub manager listener
private final CompletionListener subManagerListener = new CompletionListener() {
@Override
public synchronized void onComplete(boolean success) {
if(!success){
Log.e(TAG, "Sub manager failed to initialize");
}
checkState();
}
};
@Override
void checkState() {
if (permissionManager != null && fileManager != null && screenManager != null && (!lockScreenConfig.isEnabled() || lockScreenManager != null)) {
if (permissionManager.getState() == BaseSubManager.READY && fileManager.getState() == BaseSubManager.READY && screenManager.getState() == BaseSubManager.READY && (!lockScreenConfig.isEnabled() || lockScreenManager.getState() == BaseSubManager.READY)) {
DebugTool.logInfo("Starting sdl manager, all sub managers are in ready state");
transitionToState(BaseSubManager.READY);
handleQueuedNotifications();
notifyDevListener(null);
onReady();
} else if (permissionManager.getState() == BaseSubManager.ERROR && fileManager.getState() == BaseSubManager.ERROR && screenManager.getState() == BaseSubManager.ERROR && (!lockScreenConfig.isEnabled() || lockScreenManager.getState() == BaseSubManager.ERROR)) {
String info = "ERROR starting sdl manager, all sub managers are in error state";
Log.e(TAG, info);
transitionToState(BaseSubManager.ERROR);
notifyDevListener(info);
} else if (permissionManager.getState() == BaseSubManager.SETTING_UP || fileManager.getState() == BaseSubManager.SETTING_UP || screenManager.getState() == BaseSubManager.SETTING_UP || (lockScreenConfig.isEnabled() && lockScreenManager != null && lockScreenManager.getState() == BaseSubManager.SETTING_UP)) {
DebugTool.logInfo("SETTING UP sdl manager, some sub managers are still setting up");
transitionToState(BaseSubManager.SETTING_UP);
// No need to notify developer here!
} else {
Log.w(TAG, "LIMITED starting sdl manager, some sub managers are in error or limited state and the others finished setting up");
transitionToState(BaseSubManager.LIMITED);
handleQueuedNotifications();
notifyDevListener(null);
onReady();
}
} else {
// We should never be here, but somehow one of the sub-sub managers is null
String info = "ERROR one of the sdl sub managers is null";
Log.e(TAG, info);
transitionToState(BaseSubManager.ERROR);
notifyDevListener(info);
}
}
private void notifyDevListener(String info) {
if (managerListener != null) {
if (getState() == BaseSubManager.ERROR){
managerListener.onError(info, null);
} else {
managerListener.onStart();
}
}
}
private void onReady(){
// Set the app icon
if (SdlManager.this.appIcon != null && SdlManager.this.appIcon.getName() != null) {
if (fileManager != null && fileManager.getState() == BaseSubManager.READY && !fileManager.hasUploadedFile(SdlManager.this.appIcon)) {
fileManager.uploadArtwork(SdlManager.this.appIcon, new CompletionListener() {
@Override
public void onComplete(boolean success) {
if (success) {
SetAppIcon msg = new SetAppIcon(SdlManager.this.appIcon.getName());
_internalInterface.sendRPCRequest(msg);
}
}
});
} else {
SetAppIcon msg = new SetAppIcon(SdlManager.this.appIcon.getName());
_internalInterface.sendRPCRequest(msg);
}
}
}
@Override
protected void checkLifecycleConfiguration(){
final Language actualLanguage = this.getRegisterAppInterfaceResponse().getLanguage();
if (actualLanguage != null && !actualLanguage.equals(hmiLanguage)) {
final LifecycleConfigurationUpdate lcu = managerListener.managerShouldUpdateLifecycle(actualLanguage);
if (lcu != null) {
ChangeRegistration changeRegistration = new ChangeRegistration(actualLanguage, actualLanguage);
changeRegistration.setAppName(lcu.getAppName());
changeRegistration.setNgnMediaScreenAppName(lcu.getShortAppName());
changeRegistration.setTtsName(lcu.getTtsName());
changeRegistration.setVrSynonyms(lcu.getVoiceRecognitionCommandNames());
changeRegistration.setOnRPCResponseListener(new OnRPCResponseListener() {
@Override
public void onResponse(int correlationId, RPCResponse response) {
if (response.getSuccess()){
// go through and change sdlManager properties that were changed via the LCU update
hmiLanguage = actualLanguage;
if (lcu.getAppName() != null) {
appName = lcu.getAppName();
}
if (lcu.getShortAppName() != null) {
shortAppName = lcu.getShortAppName();
}
if (lcu.getTtsName() != null) {
ttsChunks = lcu.getTtsName();
}
if (lcu.getVoiceRecognitionCommandNames() != null) {
vrSynonyms = lcu.getVoiceRecognitionCommandNames();
}
}
try {
DebugTool.logInfo(response.serializeJSON().toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void onError(int correlationId, Result resultCode, String info) {
DebugTool.logError("Change Registration onError: " + resultCode + " | Info: " + info);
changeRegistrationRetry++;
if (changeRegistrationRetry < MAX_RETRY) {
final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
@Override
public void run() {
checkLifecycleConfiguration();
DebugTool.logInfo("Retry Change Registration Count: " + changeRegistrationRetry);
}
}, 3000);
}
}
});
this.sendRPC(changeRegistration);
}
}
}
@Override
protected void initialize(){
// Instantiate sub managers
this.permissionManager = new PermissionManager(_internalInterface);
this.fileManager = new FileManager(_internalInterface, context, fileManagerConfig);
if (lockScreenConfig.isEnabled()) {
this.lockScreenManager = new LockScreenManager(lockScreenConfig, context, _internalInterface);
}
this.screenManager = new ScreenManager(_internalInterface, this.fileManager);
if(getAppTypes().contains(AppHMIType.NAVIGATION) || getAppTypes().contains(AppHMIType.PROJECTION)){
this.videoStreamManager = new VideoStreamManager(_internalInterface);
} else {
this.videoStreamManager = null;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
&& (getAppTypes().contains(AppHMIType.NAVIGATION) || getAppTypes().contains(AppHMIType.PROJECTION)) ) {
this.audioStreamManager = new AudioStreamManager(_internalInterface, context);
} else {
this.audioStreamManager = null;
}
// Start sub managers
this.permissionManager.start(subManagerListener);
this.fileManager.start(subManagerListener);
if (lockScreenConfig.isEnabled()){
this.lockScreenManager.start(subManagerListener);
}
this.screenManager.start(subManagerListener);
}
/** Dispose SdlManager and clean its resources
* Note: new instance of SdlManager should be created on every connection. SdlManager cannot be reused after getting disposed.
*/
@SuppressLint("NewApi")
@Override
public void dispose() {
if (this.permissionManager != null) {
this.permissionManager.dispose();
}
if (this.fileManager != null) {
this.fileManager.dispose();
}
if (this.lockScreenManager != null) {
this.lockScreenManager.dispose();
}
if (this.screenManager != null) {
this.screenManager.dispose();
}
if(this.videoStreamManager != null) {
this.videoStreamManager.dispose();
}
// SuppressLint("NewApi") is used because audioStreamManager is only available on android >= jelly bean
if (this.audioStreamManager != null) {
this.audioStreamManager.dispose();
}
if (this.proxy != null && !proxy.isDisposed()) {
try {
this.proxy.dispose();
} catch (SdlException e) {
DebugTool.logError("Issue disposing proxy in SdlManager", e);
}
}
if(managerListener != null){
managerListener.onDestroy();
managerListener = null;
}
transitionToState(BaseSubManager.SHUTDOWN);
}
// MANAGER GETTERS
/**
* Gets the PermissionManager.
* Note: PermissionManager should be used only after SdlManager.start() CompletionListener callback is completed successfully.
* @return a PermissionManager object
*/
public PermissionManager getPermissionManager() {
if (permissionManager.getState() != BaseSubManager.READY && permissionManager.getState() != BaseSubManager.LIMITED){
Log.e(TAG,"PermissionManager should not be accessed because it is not in READY/LIMITED state");
}
checkSdlManagerState();
return permissionManager;
}
/**
* Gets the FileManager.
* Note: FileManager should be used only after SdlManager.start() CompletionListener callback is completed successfully.
* @return a FileManager object
*/
public FileManager getFileManager() {
if (fileManager.getState() != BaseSubManager.READY && fileManager.getState() != BaseSubManager.LIMITED){
Log.e(TAG, "FileManager should not be accessed because it is not in READY/LIMITED state");
}
checkSdlManagerState();
return fileManager;
}
/**
* Gets the VideoStreamManager.
* The VideoStreamManager returned will only be not null if the registered app type is
* either NAVIGATION or PROJECTION. Once the VideoStreamManager is retrieved, its start()
* method will need to be called before use.
*
Note: VideoStreamManager should be used only after SdlManager.start() CompletionListener callback is completed successfully.
* @return a VideoStreamManager object attached to this SdlManager instance
*/
public @Nullable
VideoStreamManager getVideoStreamManager() {
checkSdlManagerState();
return videoStreamManager;
}
/**
* Gets the AudioStreamManager.
* The AudioStreamManager returned will only be not null if the registered app type is
* either NAVIGATION or PROJECTION. Once the AudioStreamManager is retrieved, its start()
* method will need to be called before use.
*
Note: AudioStreamManager should be used only after SdlManager.start() CompletionListener callback is completed successfully.
* @return a AudioStreamManager object
*/
public @Nullable AudioStreamManager getAudioStreamManager() {
checkSdlManagerState();
return audioStreamManager;
}
/**
* Gets the ScreenManager.
* Note: ScreenManager should be used only after SdlManager.start() CompletionListener callback is completed successfully.
* @return a ScreenManager object
*/
public ScreenManager getScreenManager() {
if (screenManager.getState() != BaseSubManager.READY && screenManager.getState() != BaseSubManager.LIMITED){
Log.e(TAG, "ScreenManager should not be accessed because it is not in READY/LIMITED state");
}
checkSdlManagerState();
return screenManager;
}
/**
* Gets the LockScreenManager.
* Note: LockScreenManager should be used only after SdlManager.start() CompletionListener callback is completed successfully.
* @return a LockScreenManager object
*/
public LockScreenManager getLockScreenManager() {
if (lockScreenManager.getState() != BaseSubManager.READY && lockScreenManager.getState() != BaseSubManager.LIMITED){
Log.e(TAG, "LockScreenManager should not be accessed because it is not in READY/LIMITED state");
}
checkSdlManagerState();
return lockScreenManager;
}
/**
* Gets the SystemCapabilityManager.
* Note: SystemCapabilityManager should be used only after SdlManager.start() CompletionListener callback is completed successfully.
* @return a SystemCapabilityManager object
*/
public SystemCapabilityManager getSystemCapabilityManager(){
return proxy.getSystemCapabilityManager();
}
/**
* Method to retrieve the RegisterAppInterface Response message that was sent back from the
* module. It contains various attributes about the connected module and can be used to adapt
* to different module types and their supported features.
*
* @return RegisterAppInterfaceResponse received from the module or null if the app has not yet
* registered with the module.
*/
@Override
public RegisterAppInterfaceResponse getRegisterAppInterfaceResponse(){
if(proxy != null){
return proxy.getRegisterAppInterfaceResponse();
}
return null;
}
/**
* Get the current OnHMIStatus
* @return OnHMIStatus object represents the current OnHMIStatus
*/
@Override
public OnHMIStatus getCurrentHMIStatus(){
if(this.proxy !=null ){
return proxy.getCurrentHMIStatus();
}
return null;
}
/**
* Retrieves the auth token, if any, that was attached to the StartServiceACK for the RPC
* service from the module. For example, this should be used to login to a user account.
* @return the string representation of the auth token
*/
@Override
public String getAuthToken(){
return this.proxy.getAuthToken();
}
// PROTECTED GETTERS
protected LockScreenConfig getLockScreenConfig() { return lockScreenConfig; }
protected FileManagerConfig getFileManagerConfig() { return fileManagerConfig; }
// SENDING REQUESTS
/**
* Send RPC Message
* @param message RPCMessage
*/
@Override
public void sendRPC(RPCMessage message) {
try{
proxy.sendRPC(message);
}catch (SdlException exception){
handleSdlException(exception);
}
}
/**
* Takes a list of RPCMessages and sends it to SDL in a synchronous fashion. Responses are captured through callback on OnMultipleRequestListener.
* For sending requests asynchronously, use sendRequests
*
* NOTE: This will override any listeners on individual RPCs
*
* ADDITIONAL NOTE: This only takes the type of RPCRequest for now, notifications and responses will be thrown out
*
* @param rpcs is the list of RPCMessages being sent
* @param listener listener for updates and completions
*/
@Override
public void sendSequentialRPCs(final List extends RPCMessage> rpcs, final OnMultipleRequestListener listener){
List rpcRequestList = new ArrayList<>();
for (int i = 0; i < rpcs.size(); i++) {
if (rpcs.get(i) instanceof RPCRequest){
rpcRequestList.add((RPCRequest)rpcs.get(i));
}
}
if (rpcRequestList.size() > 0) {
try{
proxy.sendSequentialRequests(rpcRequestList, listener);
}catch (SdlException exception){
handleSdlException(exception);
}
}
}
/**
* Takes a list of RPCMessages and sends it to SDL. Responses are captured through callback on OnMultipleRequestListener.
* For sending requests synchronously, use sendSequentialRPCs
*
* NOTE: This will override any listeners on individual RPCs
*
* ADDITIONAL NOTE: This only takes the type of RPCRequest for now, notifications and responses will be thrown out
*
* @param rpcs is the list of RPCMessages being sent
* @param listener listener for updates and completions
*/
@Override
public void sendRPCs(List extends RPCMessage> rpcs, final OnMultipleRequestListener listener) {
List rpcRequestList = new ArrayList<>();
for (int i = 0; i < rpcs.size(); i++) {
if (rpcs.get(i) instanceof RPCRequest){
rpcRequestList.add((RPCRequest)rpcs.get(i));
}
}
if (rpcRequestList.size() > 0) {
try{
proxy.sendRequests(rpcRequestList, listener);
}catch (SdlException exception){
handleSdlException(exception);
}
}
}
private void handleSdlException(SdlException exception){
if(exception != null){
DebugTool.logError("Caught SdlException: " + exception.getSdlExceptionCause());
// In the future this should handle logic to dispose the manager if it is an unrecoverable error
}else{
DebugTool.logError("Caught SdlException" );
}
}
/**
* Add an OnRPCNotificationListener
* @param listener listener that will be called when a notification is received
*/
@Override
public void addOnRPCNotificationListener(FunctionID notificationId, OnRPCNotificationListener listener){
proxy.addOnRPCNotificationListener(notificationId,listener);
}
/**
* Remove an OnRPCNotificationListener
* @param listener listener that was previously added
*/
@Override
public void removeOnRPCNotificationListener(FunctionID notificationId, OnRPCNotificationListener listener){
proxy.removeOnRPCNotificationListener(notificationId, listener);
}
/**
* Add an OnRPCRequestListener
* @param listener listener that will be called when a request is received
*/
@Override
public void addOnRPCRequestListener(FunctionID requestId, OnRPCRequestListener listener){
proxy.addOnRPCRequestListener(requestId,listener);
}
/**
* Remove an OnRPCRequestListener
* @param listener listener that was previously added
*/
@Override
public void removeOnRPCRequestListener(FunctionID requestId, OnRPCRequestListener listener){
proxy.removeOnRPCRequestListener(requestId, listener);
}
// LIFECYCLE / OTHER
// STARTUP
/**
* Starts up a SdlManager, and calls provided callback called once all BaseSubManagers are done setting up
*/
@SuppressWarnings("unchecked")
@Override
public void start(){
if (proxy == null) {
try {
if(transport!= null && transport.getTransportType() == TransportType.MULTIPLEX){
//Do the thing
MultiplexTransportConfig multiplexTransportConfig = (MultiplexTransportConfig)(transport);
final MultiplexTransportConfig.TransportListener devListener = multiplexTransportConfig.getTransportListener();
multiplexTransportConfig.setTransportListener(new MultiplexTransportConfig.TransportListener() {
@Override
public void onTransportEvent(List connectedTransports, boolean audioStreamTransportAvail, boolean videoStreamTransportAvail) {
//Pass to submanagers that need it
if(videoStreamManager != null){
videoStreamManager.handleTransportUpdated(connectedTransports, audioStreamTransportAvail, videoStreamTransportAvail);
}
if(audioStreamManager != null){
audioStreamManager.handleTransportUpdated(connectedTransports, audioStreamTransportAvail, videoStreamTransportAvail);
}
//If the developer supplied a listener to start, it is time to call that
if(devListener != null){
devListener.onTransportEvent(connectedTransports,audioStreamTransportAvail,videoStreamTransportAvail);
}
}
});
//If the requires audio support has not been set, it should be set to true if the
//app is a media app, and false otherwise
if(multiplexTransportConfig.requiresAudioSupport() == null){
multiplexTransportConfig.setRequiresAudioSupport(isMediaApp);
}
}
proxy = new SdlProxyBase(proxyBridge, context, appName, shortAppName, isMediaApp, hmiLanguage,
hmiLanguage, hmiTypes, appId, transport, vrSynonyms, ttsChunks, dayColorScheme,
nightColorScheme) {};
proxy.setMinimumProtocolVersion(minimumProtocolVersion);
proxy.setMinimumRPCVersion(minimumRPCVersion);
if (sdlSecList != null && !sdlSecList.isEmpty()) {
proxy.setSdlSecurity(sdlSecList, serviceEncryptionListener);
}
//Setup the notification queue
initNotificationQueue();
} catch (SdlException e) {
transitionToState(BaseSubManager.ERROR);
if (managerListener != null) {
managerListener.onError("Unable to start manager", e);
}
}
}
}
protected void setProxy(SdlProxyBase proxy){
this.proxy = proxy;
}
// INTERNAL INTERFACE
private ISdl _internalInterface = new ISdl() {
@Override
public void start() {
try{
proxy.initializeProxy();
}catch (SdlException e){
e.printStackTrace();
}
}
@Override
public void stop() {
try{
proxy.dispose();
}catch (SdlException e){
e.printStackTrace();
}
}
@Override
public boolean isConnected() {
return proxy.getIsConnected();
}
@Override
public void addServiceListener(SessionType serviceType, ISdlServiceListener sdlServiceListener) {
proxy.addServiceListener(serviceType,sdlServiceListener);
}
@Override
public void removeServiceListener(SessionType serviceType, ISdlServiceListener sdlServiceListener) {
proxy.removeServiceListener(serviceType,sdlServiceListener);
}
@Override
public void startVideoService(VideoStreamingParameters parameters, boolean encrypted) {
if(proxy.getIsConnected()){
proxy.startVideoService(encrypted,parameters);
}
}
@Override
public IVideoStreamListener startVideoStream(boolean isEncrypted, VideoStreamingParameters parameters){
if(proxy.getIsConnected()){
return proxy.startVideoStream(isEncrypted, parameters);
}else{
DebugTool.logError("Unable to start video stream, proxy not connected");
return null;
}
}
@Override
public void stopVideoService() {
if(proxy.getIsConnected()){
proxy.endVideoStream();
}
}
@Override
public void startAudioService(boolean isEncrypted, AudioStreamingCodec codec,
AudioStreamingParams params) {
if(proxy.getIsConnected()){
proxy.startAudioStream(isEncrypted, codec, params);
}
}
@Override
public void startAudioService(boolean encrypted) {
if(isConnected()){
proxy.startService(SessionType.PCM, encrypted);
}
}
@Override
public IAudioStreamListener startAudioStream(boolean isEncrypted, AudioStreamingCodec codec,
AudioStreamingParams params) {
return proxy.startAudioStream(isEncrypted, codec, params);
}
@Override
public void stopAudioService() {
if(proxy.getIsConnected()){
proxy.endAudioStream();
}
}
@Override
public void sendRPCRequest(RPCRequest message){
try {
proxy.sendRPC(message);
} catch (SdlException e) {
e.printStackTrace();
}
}
@Override
public void sendRPC(RPCMessage message) {
try {
proxy.sendRPC(message);
} catch (SdlException e) {
e.printStackTrace();
}
}
@Override
public void sendRequests(List extends RPCRequest> rpcs, OnMultipleRequestListener listener) {
try {
proxy.sendRequests(rpcs, listener);
} catch (SdlException e) {
e.printStackTrace();
}
}
@Override
public void sendSequentialRPCs(List extends RPCMessage> rpcs, OnMultipleRequestListener listener) {
try {
proxy.sendSequentialRequests(rpcs,listener);
} catch (SdlException e) {
DebugTool.logError("Issue sending sequential RPCs ", e);
}
}
@Override
public void addOnRPCNotificationListener(FunctionID notificationId, OnRPCNotificationListener listener) {
proxy.addOnRPCNotificationListener(notificationId,listener);
}
@Override
public boolean removeOnRPCNotificationListener(FunctionID notificationId, OnRPCNotificationListener listener) {
return proxy.removeOnRPCNotificationListener(notificationId,listener);
}
@Override
public void addOnRPCRequestListener(FunctionID functionID, OnRPCRequestListener listener) {
proxy.addOnRPCRequestListener(functionID, listener);
}
@Override
public boolean removeOnRPCRequestListener(FunctionID functionID, OnRPCRequestListener listener) {
return proxy.removeOnRPCRequestListener(functionID, listener);
}
@Override
public void addOnRPCListener(final FunctionID responseId, final OnRPCListener listener) {
proxyBridge.addRpcListener(responseId, listener);
}
@Override
public boolean removeOnRPCListener(final FunctionID responseId, final OnRPCListener listener) {
return proxyBridge.removeOnRPCListener(responseId, listener);
}
@Override
public Object getCapability(SystemCapabilityType systemCapabilityType){
return proxy.getCapability(systemCapabilityType);
}
@Override
public void getCapability(SystemCapabilityType systemCapabilityType, OnSystemCapabilityListener scListener) {
proxy.getCapability(systemCapabilityType, scListener);
}
@Override
public RegisterAppInterfaceResponse getRegisterAppInterfaceResponse() {
return proxy.getRegisterAppInterfaceResponse();
}
@Override
public Object getCapability(SystemCapabilityType systemCapabilityType, OnSystemCapabilityListener scListener, boolean forceUpdate) {
if (proxy != null && proxy.getSystemCapabilityManager() != null) {
return proxy.getSystemCapabilityManager().getCapability(systemCapabilityType, scListener, forceUpdate);
}
return null;
}
@Override
public boolean isCapabilitySupported(SystemCapabilityType systemCapabilityType){
return proxy.isCapabilitySupported(systemCapabilityType);
}
@Override
public void addOnSystemCapabilityListener(SystemCapabilityType systemCapabilityType, OnSystemCapabilityListener listener) {
proxy.addOnSystemCapabilityListener(systemCapabilityType, listener);
}
@Override
public boolean removeOnSystemCapabilityListener(SystemCapabilityType systemCapabilityType, OnSystemCapabilityListener listener) {
return proxy.removeOnSystemCapabilityListener(systemCapabilityType, listener);
}
@Override
public boolean isTransportForServiceAvailable(SessionType serviceType) {
if(SessionType.NAV.equals(serviceType)){
return proxy.isVideoStreamTransportAvailable();
}else if(SessionType.PCM.equals(serviceType)){
return proxy.isAudioStreamTransportAvailable();
}
return false;
}
@Override
public SdlMsgVersion getSdlMsgVersion(){
try {
return proxy.getSdlMsgVersion();
} catch (SdlException e) {
e.printStackTrace();
}
return null;
}
@Override
public @NonNull Version getProtocolVersion() {
if(proxy.getProtocolVersion() != null){
return proxy.getProtocolVersion();
}else{
return new Version(1,0,0);
}
}
@Override
public void startRPCEncryption() {
if (proxy != null) {
proxy.startProtectedRPCService();
}
}
};
// BUILDER
public static class Builder {
SdlManager sdlManager;
/**
* Builder for the SdlManager. Parameters in the constructor are required.
* @param context the current context
* @param appId the app's ID
* @param appName the app's name
* @param listener a SdlManagerListener object
*/
public Builder(@NonNull Context context, @NonNull final String appId, @NonNull final String appName, @NonNull final SdlManagerListener listener){
sdlManager = new SdlManager();
setContext(context);
setAppId(appId);
setAppName(appName);
setManagerListener(listener);
}
/**
* Builder for the SdlManager. Parameters in the constructor are required.
* @param context the current context
* @param appId the app's ID
* @param appName the app's name
* @param listener a SdlManagerListener object
*/
public Builder(@NonNull Context context, @NonNull final String appId, @NonNull final String appName, @NonNull BaseTransportConfig transport, @NonNull final SdlManagerListener listener){
sdlManager = new SdlManager();
setContext(context);
setAppId(appId);
setAppName(appName);
setTransportType(transport);
setManagerListener(listener);
}
/**
* Sets the App ID
* @param appId
*/
public Builder setAppId(@NonNull final String appId){
sdlManager.appId = appId;
return this;
}
/**
* Sets the Application Name
* @param appName
*/
public Builder setAppName(@NonNull final String appName){
sdlManager.appName = appName;
return this;
}
/**
* Sets the Short Application Name
* @param shortAppName
*/
public Builder setShortAppName(final String shortAppName) {
sdlManager.shortAppName = shortAppName;
return this;
}
/**
* Sets the minimum protocol version that will be permitted to connect.
* If the protocol version of the head unit connected is below this version,
* the app will disconnect with an EndService protocol message and will not register.
* @param minimumProtocolVersion the minimum Protocol spec version that should be accepted
*/
public Builder setMinimumProtocolVersion(final Version minimumProtocolVersion) {
sdlManager.minimumProtocolVersion = minimumProtocolVersion;
return this;
}
/**
* The minimum RPC version that will be permitted to connect.
* If the RPC version of the head unit connected is below this version, an UnregisterAppInterface will be sent.
* @param minimumRPCVersion the minimum RPC spec version that should be accepted
*/
public Builder setMinimumRPCVersion(final Version minimumRPCVersion) {
sdlManager.minimumRPCVersion = minimumRPCVersion;
return this;
}
/**
* Sets the Language of the App
* @param hmiLanguage the desired language to be used on the display/HMI of the connected module
*/
public Builder setLanguage(final Language hmiLanguage){
sdlManager.hmiLanguage = hmiLanguage;
return this;
}
/**
* Sets the TemplateColorScheme for daytime
* @param dayColorScheme color scheme that will be used (if supported) when the display is
* in a "Day Mode" or similar. Should comprise of colors that contrast
* well during the day under sunlight.
*/
public Builder setDayColorScheme(final TemplateColorScheme dayColorScheme){
sdlManager.dayColorScheme = dayColorScheme;
return this;
}
/**
* Sets the TemplateColorScheme for nighttime
* @param nightColorScheme color scheme that will be used (if supported) when the display is
* in a "Night Mode" or similar. Should comprise of colors that
* contrast well during the night and are not brighter than average.
*/
public Builder setNightColorScheme(final TemplateColorScheme nightColorScheme){
sdlManager.nightColorScheme = nightColorScheme;
return this;
}
/**
* Sets the FileManagerConfig for the session.
* Note: If not set, the default configuration value of 1 will be set for
* artworkRetryCount and fileRetryCount in FileManagerConfig
* @param fileManagerConfig - configuration options
*/
public Builder setFileManagerConfig (final FileManagerConfig fileManagerConfig){
sdlManager.fileManagerConfig = fileManagerConfig;
return this;
}
/**
* Sets the LockScreenConfig for the session.
* Note: If not set, the default configuration will be used.
* @param lockScreenConfig - configuration options
*/
public Builder setLockScreenConfig (final LockScreenConfig lockScreenConfig){
sdlManager.lockScreenConfig = lockScreenConfig;
return this;
}
/**
* Sets the icon for the app on head unit / In-Vehicle-Infotainment system
* @param sdlArtwork the icon that will be used to represent this application on the
* connected module
*/
public Builder setAppIcon(final SdlArtwork sdlArtwork){
sdlManager.appIcon = sdlArtwork;
return this;
}
/**
* Sets the vector of AppHMIType
* Note: This should be an ordered list from most -> least relevant
* @param hmiTypes HMI types that represent this application. For example, if the app is a
* music player, the MEDIA HMIType should be included.
*/
public Builder setAppTypes(final Vector hmiTypes){
sdlManager.hmiTypes = hmiTypes;
if (hmiTypes != null) {
sdlManager.isMediaApp = hmiTypes.contains(AppHMIType.MEDIA);
}
return this;
}
/**
* Sets the voice recognition synonyms that can be used to identify this application.
* @param vrSynonyms a vector of Strings that can be associated with this app. For example the app's name should
* be included as well as any phonetic spellings of the app name that might help the on-board
* VR system associated a users spoken word with the supplied synonyms.
*/
public Builder setVrSynonyms(final Vector vrSynonyms) {
sdlManager.vrSynonyms = vrSynonyms;
return this;
}
/**
* Sets the Text-To-Speech Name of the application. These TTSChunks might be used by the module as an audio
* representation of the app's name.
* @param ttsChunks the TTS chunks that can represent this app's name
*/
public Builder setTtsName(final Vector ttsChunks) {
sdlManager.ttsChunks = ttsChunks;
return this;
}
/**
* This Object type may change with the transport refactor
* Sets the BaseTransportConfig
* @param transport the type of transport that should be used for this SdlManager instance.
*/
public Builder setTransportType(@NonNull BaseTransportConfig transport){
sdlManager.transport = transport;
return this;
}
/**
* Sets the Context
* @param context
*/
public Builder setContext(Context context){
sdlManager.context = context;
return this;
}
/**
* Sets the Security library
* @param secList The list of security class(es)
*/
@Deprecated
public Builder setSdlSecurity(List> secList) {
sdlManager.sdlSecList = secList;
return this;
}
/**
* Sets the security libraries and a callback to notify caller when there is update to encryption service
* @param secList The list of security class(es)
* @param listener The callback object
*/
public Builder setSdlSecurity(@NonNull List> secList, ServiceEncryptionListener listener) {
sdlManager.sdlSecList = secList;
sdlManager.serviceEncryptionListener = listener;
return this;
}
/**
* Set the SdlManager Listener
* @param listener the listener
*/
public Builder setManagerListener(@NonNull final SdlManagerListener listener){
sdlManager.managerListener = listener;
return this;
}
/**
* Set RPCNotification listeners. SdlManager will preload these listeners before any RPCs are sent/received.
* @param listeners a map of listeners that will be called when a notification is received.
* Key represents the FunctionID of the notification and value represents the listener
*/
public Builder setRPCNotificationListeners(Map listeners){
sdlManager.onRPCNotificationListeners = listeners;
return this;
}
/**
* Build SdlManager ang get it ready to be started
* Note: new instance of SdlManager should be created on every connection. SdlManager cannot be reused after getting disposed.
* @return SdlManager instance that is ready to be started
*/
public SdlManager build() {
if (sdlManager.appName == null) {
throw new IllegalArgumentException("You must specify an app name by calling setAppName");
}
if (sdlManager.appId == null) {
throw new IllegalArgumentException("You must specify an app ID by calling setAppId");
}
if (sdlManager.managerListener == null) {
throw new IllegalArgumentException("You must set a SdlManagerListener object");
}
if (sdlManager.transport == null) {
throw new IllegalArgumentException("You must set a transport type object");
}
if (sdlManager.hmiTypes == null) {
Vector hmiTypesDefault = new Vector<>();
hmiTypesDefault.add(AppHMIType.DEFAULT);
sdlManager.hmiTypes = hmiTypesDefault;
sdlManager.isMediaApp = false;
}
if (sdlManager.lockScreenConfig == null){
// if lock screen params are not set, use default
sdlManager.lockScreenConfig = new LockScreenConfig();
}
if(sdlManager.fileManagerConfig == null){
//if FileManagerConfig is not set use default
sdlManager.fileManagerConfig = new FileManagerConfig();
}
if (sdlManager.hmiLanguage == null){
sdlManager.hmiLanguage = Language.EN_US;
}
if (sdlManager.minimumProtocolVersion == null){
sdlManager.minimumProtocolVersion = new Version("1.0.0");
}
if (sdlManager.minimumRPCVersion == null){
sdlManager.minimumRPCVersion = new Version("1.0.0");
}
sdlManager.transitionToState(BaseSubManager.SETTING_UP);
return sdlManager;
}
}
/**
* Start a secured RPC service
*/
public void startRPCEncryption() {
if (proxy != null) {
proxy.startProtectedRPCService();
}
}
}