diff options
author | RHenigan <heniganr1@gmail.com> | 2020-06-30 14:54:13 -0400 |
---|---|---|
committer | RHenigan <heniganr1@gmail.com> | 2020-06-30 14:54:13 -0400 |
commit | fc067f00f182afe13876272ba4a56f912b53d5c1 (patch) | |
tree | b840e8fbee18c88eccf5d04c70588643c161d4a8 | |
parent | e58a6e712a0a2e99d207aee8e7329da247a7eaab (diff) | |
parent | 7a2f581925c213a07fbbe0fac2eb7aa6a40e738d (diff) | |
download | sdl_android-fc067f00f182afe13876272ba4a56f912b53d5c1.tar.gz |
Merge branch 'develop' into bugfix/issue_1377_logging
14 files changed, 816 insertions, 404 deletions
diff --git a/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java b/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java index ec8afa812..893525b97 100755 --- a/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java +++ b/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java @@ -12,6 +12,7 @@ import android.os.IBinder; import android.util.Log; import com.smartdevicelink.managers.CompletionListener; +import com.smartdevicelink.managers.screen.OnButtonListener; import com.smartdevicelink.managers.SdlManager; import com.smartdevicelink.managers.SdlManagerListener; import com.smartdevicelink.managers.file.filetypes.SdlArtwork; @@ -27,9 +28,12 @@ import com.smartdevicelink.protocol.enums.FunctionID; import com.smartdevicelink.proxy.RPCNotification; import com.smartdevicelink.proxy.TTSChunkFactory; import com.smartdevicelink.proxy.rpc.Alert; +import com.smartdevicelink.proxy.rpc.OnButtonEvent; +import com.smartdevicelink.proxy.rpc.OnButtonPress; import com.smartdevicelink.proxy.rpc.OnHMIStatus; import com.smartdevicelink.proxy.rpc.Speak; import com.smartdevicelink.proxy.rpc.enums.AppHMIType; +import com.smartdevicelink.proxy.rpc.enums.ButtonName; import com.smartdevicelink.proxy.rpc.enums.FileType; import com.smartdevicelink.proxy.rpc.enums.HMILevel; import com.smartdevicelink.proxy.rpc.enums.InteractionMode; @@ -183,6 +187,7 @@ public class SdlService extends Service { performWelcomeSpeak(); performWelcomeShow(); preloadChoices(); + subscribeToButtons(); } } }); @@ -371,6 +376,36 @@ public class SdlService extends Service { } /** + * Attempts to Subscribe to all preset buttons + */ + private void subscribeToButtons() { + ButtonName[] buttonNames = {ButtonName.PLAY_PAUSE, ButtonName.SEEKLEFT, ButtonName.SEEKRIGHT, ButtonName.AC_MAX, ButtonName.AC, ButtonName.RECIRCULATE, + ButtonName.FAN_UP, ButtonName.FAN_DOWN, ButtonName.TEMP_UP, ButtonName.TEMP_DOWN, ButtonName.FAN_DOWN, ButtonName.DEFROST_MAX, ButtonName.DEFROST_REAR, ButtonName.DEFROST, + ButtonName.UPPER_VENT, ButtonName.LOWER_VENT, ButtonName.VOLUME_UP, ButtonName.VOLUME_DOWN, ButtonName.EJECT, ButtonName.SOURCE, ButtonName.SHUFFLE, ButtonName.REPEAT}; + + OnButtonListener onButtonListener = new OnButtonListener() { + @Override + public void onPress(ButtonName buttonName, OnButtonPress buttonPress) { + sdlManager.getScreenManager().setTextField1(buttonName + " pressed"); + } + + @Override + public void onEvent(ButtonName buttonName, OnButtonEvent buttonEvent) { + sdlManager.getScreenManager().setTextField2(buttonName + " " + buttonEvent.getButtonEventMode()); + } + + @Override + public void onError(String info) { + Log.i(TAG, "onError: " + info); + } + }; + + for (ButtonName buttonName : buttonNames) { + sdlManager.getScreenManager().addButtonListener(buttonName, onButtonListener); + } + } + + /** * Will show a sample test message on screen as well as speak a sample test message */ private void showTest(){ diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SubscribeButtonManagerTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SubscribeButtonManagerTest.java new file mode 100644 index 000000000..edca41770 --- /dev/null +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SubscribeButtonManagerTest.java @@ -0,0 +1,158 @@ +package com.smartdevicelink.managers.screen; + +import com.smartdevicelink.AndroidTestCase2; +import com.smartdevicelink.managers.BaseSubManager; +import com.smartdevicelink.proxy.RPCMessage; +import com.smartdevicelink.proxy.RPCRequest; +import com.smartdevicelink.proxy.interfaces.ISdl; +import com.smartdevicelink.proxy.rpc.OnButtonEvent; +import com.smartdevicelink.proxy.rpc.OnButtonPress; +import com.smartdevicelink.proxy.rpc.SubscribeButton; +import com.smartdevicelink.proxy.rpc.SubscribeButtonResponse; +import com.smartdevicelink.proxy.rpc.UnsubscribeButton; +import com.smartdevicelink.proxy.rpc.UnsubscribeButtonResponse; +import com.smartdevicelink.proxy.rpc.enums.ButtonName; +import com.smartdevicelink.proxy.rpc.enums.Result; + +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; + +public class SubscribeButtonManagerTest extends AndroidTestCase2 { + private SubscribeButtonManager subscribeButtonManager; + private ISdl internalInterface; + + private Answer<Void> onSubscribe_UnsubscribeSuccess = new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + RPCRequest message = (RPCRequest) args[0]; + if(message instanceof SubscribeButton){ + SubscribeButtonResponse subscribeButtonResponse = new SubscribeButtonResponse(); + subscribeButtonResponse.setSuccess(true); + message.getOnRPCResponseListener().onResponse(message.getCorrelationID(),subscribeButtonResponse); + } + if(message instanceof UnsubscribeButton) { + UnsubscribeButtonResponse unsubscribeButtonResponse = new UnsubscribeButtonResponse(); + unsubscribeButtonResponse.setSuccess(true); + message.getOnRPCResponseListener().onResponse(message.getCorrelationID(), unsubscribeButtonResponse); + } + return null; + } + }; + + + private Answer<Void> onSubscribeFail = new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + RPCRequest message = (RPCRequest) args[0]; + if(message instanceof SubscribeButton){ + SubscribeButtonResponse subscribeButtonResponse = new SubscribeButtonResponse(); + subscribeButtonResponse.setSuccess(false); + message.getOnRPCResponseListener().onError(message.getCorrelationID(), Result.GENERIC_ERROR, "Fail"); + } + return null; + } + }; + private OnButtonListener listener = new OnButtonListener() { + @Override + public void onPress(ButtonName buttonName, OnButtonPress buttonPress) { + + } + + @Override + public void onEvent(ButtonName buttonName, OnButtonEvent buttonEvent) { + + } + + @Override + public void onError(String info) { + + } + }; + + private OnButtonListener listener2 = new OnButtonListener() { + @Override + public void onPress(ButtonName buttonName, OnButtonPress buttonPress) { + + } + + @Override + public void onEvent(ButtonName buttonName, OnButtonEvent buttonEvent) { + + } + + @Override + public void onError(String info) { + + } + }; + + @Override + public void setUp() throws Exception { + super.setUp(); + internalInterface = mock(ISdl.class); + subscribeButtonManager = new SubscribeButtonManager(internalInterface); + } + + public void testInstantiation(){ + assertNotNull(subscribeButtonManager.onButtonListeners); + assertEquals(subscribeButtonManager.getState(), BaseSubManager.SETTING_UP); + } + + public void testDispose() { + subscribeButtonManager.addButtonListener(ButtonName.VOLUME_UP, listener); + subscribeButtonManager.dispose(); + assertTrue(subscribeButtonManager.onButtonListeners.size() == 0); + } + + public void testAddButtonListener() { + doAnswer(onSubscribe_UnsubscribeSuccess).when(internalInterface).sendRPC(any(RPCMessage.class)); + + subscribeButtonManager.addButtonListener(null, null); + assertTrue(subscribeButtonManager.onButtonListeners.size() == 0); + + subscribeButtonManager.addButtonListener(null, listener); + assertTrue(subscribeButtonManager.onButtonListeners.size() == 0); + + subscribeButtonManager.addButtonListener(ButtonName.VOLUME_UP, listener); + assertTrue(subscribeButtonManager.onButtonListeners.containsKey(ButtonName.VOLUME_UP)); + + } + + public void testAddButtonListenerError(){ + doAnswer(onSubscribeFail).when(internalInterface).sendRPC(any(RPCMessage.class)); + subscribeButtonManager.addButtonListener(ButtonName.VOLUME_UP, listener); + assertFalse(subscribeButtonManager.onButtonListeners.containsKey(ButtonName.VOLUME_UP)); + } + + public void testRemoveButtonListener() { + doAnswer(onSubscribe_UnsubscribeSuccess).when(internalInterface).sendRPC(any(RPCMessage.class)); + + subscribeButtonManager.removeButtonListener(ButtonName.VOLUME_DOWN, listener); + assertFalse(subscribeButtonManager.onButtonListeners.containsKey(ButtonName.VOLUME_DOWN)); + + subscribeButtonManager.addButtonListener(ButtonName.VOLUME_UP, listener); + assertTrue(subscribeButtonManager.onButtonListeners.get(ButtonName.VOLUME_UP).size() == 1); + + subscribeButtonManager.removeButtonListener(ButtonName.VOLUME_UP, listener2); + assertTrue(subscribeButtonManager.onButtonListeners.get(ButtonName.VOLUME_UP).size() == 1); + + subscribeButtonManager.addButtonListener(ButtonName.VOLUME_UP, listener); + assertTrue(subscribeButtonManager.onButtonListeners.get(ButtonName.VOLUME_UP).size() == 1); + + subscribeButtonManager.addButtonListener(ButtonName.VOLUME_UP, listener2); + assertTrue(subscribeButtonManager.onButtonListeners.get(ButtonName.VOLUME_UP).size() == 2); + + + subscribeButtonManager.removeButtonListener(ButtonName.VOLUME_UP, listener); + assertTrue(subscribeButtonManager.onButtonListeners.get(ButtonName.VOLUME_UP).size() == 1); + + subscribeButtonManager.removeButtonListener(ButtonName.VOLUME_UP, listener2); + assertNull(subscribeButtonManager.onButtonListeners.get(ButtonName.VOLUME_UP)); + } +} diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java index 4ec43b00c..06b9abd7b 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java @@ -215,7 +215,7 @@ public class SdlManager extends BaseSdlManager { */ @SuppressLint("NewApi") @Override - public void dispose() { + public synchronized void dispose() { if (this.permissionManager != null) { this.permissionManager.dispose(); } diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java index 87453fcd9..021e5d49a 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Livio, Inc. + * Copyright (c) 2019-2020 Livio, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -53,38 +53,29 @@ import com.smartdevicelink.transport.USBTransportConfig; import com.smartdevicelink.transport.enums.TransportType; import com.smartdevicelink.util.DebugTool; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; -import java.util.concurrent.Callable; -import java.util.concurrent.Executors; -import java.util.concurrent.FutureTask; -import java.util.concurrent.ScheduledExecutorService; /** * The lifecycle manager creates a central point for all SDL session logic to converge. It should only be used by * the library itself. Usage outside the library is not permitted and will not be protected for in the future. * - * @author Bilal Alsharifi. */ @RestrictTo(RestrictTo.Scope.LIBRARY) public class LifecycleManager extends BaseLifecycleManager { - private static final int RESPONSE_WAIT_TIME = 2000; - private ISdlServiceListener navServiceListener; - private boolean navServiceStartResponseReceived = false; - private boolean navServiceStartResponse = false; - private boolean navServiceEndResponseReceived = false; - private boolean navServiceEndResponse = false; - private boolean pcmServiceEndResponseReceived = false; - private boolean pcmServiceEndResponse = false; - private Context context; + private ISdlServiceListener videoServiceListener; + private boolean videoServiceStartResponseReceived = false; + private boolean videoServiceStartResponse = false; + private WeakReference<Context> contextWeakReference; public LifecycleManager(AppConfig appConfig, BaseTransportConfig config, LifecycleListener listener) { super(appConfig, config, listener); } @Override - void initializeProxy() { - super.initializeProxy(); + void initialize() { + super.initialize(); //Handle legacy USB connections if (_transportConfig != null && TransportType.USB.equals(_transportConfig.getTransportType())) { @@ -112,9 +103,9 @@ public class LifecycleManager extends BaseLifecycleManager { } @Override - void cycleProxy(SdlDisconnectedReason disconnectedReason) { - cleanProxy(); - initializeProxy(); + void cycle(SdlDisconnectedReason disconnectedReason) { + clean(); + initialize(); if (!SdlDisconnectedReason.LEGACY_BLUETOOTH_MODE_ENABLED.equals(disconnectedReason) && !SdlDisconnectedReason.PRIMARY_TRANSPORT_CYCLE_REQUEST.equals(disconnectedReason)) { //We don't want to alert higher if we are just cycling for legacy bluetooth onClose("Sdl Proxy Cycled", new SdlException("Sdl Proxy Cycled", SdlExceptionCause.SDL_PROXY_CYCLED), disconnectedReason); @@ -130,14 +121,19 @@ public class LifecycleManager extends BaseLifecycleManager { @RestrictTo(RestrictTo.Scope.LIBRARY) public void setContext(Context context) { - this.context = context; + this.contextWeakReference = new WeakReference<>(context); } @Override void setSdlSecurityStaticVars() { super.setSdlSecurityStaticVars(); + Context context = null; Service service = null; + + if(this.contextWeakReference != null){ + context = contextWeakReference.get(); + } if (context != null && context instanceof Service) { service = (Service) context; } @@ -146,11 +142,11 @@ public class LifecycleManager extends BaseLifecycleManager { } @Override - void onProtocolSessionStarted(SessionType sessionType) { - super.onProtocolSessionStarted(sessionType); + void onServiceStarted(SessionType sessionType) { + super.onServiceStarted(sessionType); if (sessionType.eq(SessionType.NAV)) { - navServiceStartResponseReceived = true; - navServiceStartResponse = true; + videoServiceStartResponseReceived = true; + videoServiceStartResponse = true; } } @@ -160,42 +156,18 @@ public class LifecycleManager extends BaseLifecycleManager { if (availablePrimary) { _transportConfig = transportConfig; DebugTool.logInfo(TAG, "notifying RPC session ended, but potential primary transport available"); - cycleProxy(SdlDisconnectedReason.PRIMARY_TRANSPORT_CYCLE_REQUEST); + cycle(SdlDisconnectedReason.PRIMARY_TRANSPORT_CYCLE_REQUEST); } else { onClose(info, null, null); } } @Override - void onProtocolSessionStartedNACKed(SessionType sessionType) { - super.onProtocolSessionStartedNACKed(sessionType); - if (sessionType.eq(SessionType.NAV)) { - navServiceStartResponseReceived = true; - navServiceStartResponse = false; - } - } - - @Override - void onProtocolSessionEnded(SessionType sessionType) { - super.onProtocolSessionEnded(sessionType); + void onStartServiceNACKed(SessionType sessionType) { + super.onStartServiceNACKed(sessionType); if (sessionType.eq(SessionType.NAV)) { - navServiceEndResponseReceived = true; - navServiceEndResponse = true; - } else if (sessionType.eq(SessionType.PCM)) { - pcmServiceEndResponseReceived = true; - pcmServiceEndResponse = true; - } - } - - @Override - void onProtocolSessionEndedNACKed(SessionType sessionType) { - super.onProtocolSessionEndedNACKed(sessionType); - if (sessionType.eq(SessionType.NAV)) { - navServiceEndResponseReceived = true; - navServiceEndResponse = false; - } else if (sessionType.eq(SessionType.PCM)) { - pcmServiceEndResponseReceived = true; - pcmServiceEndResponse = false; + videoServiceStartResponseReceived = true; + videoServiceStartResponse = false; } } @@ -232,72 +204,60 @@ public class LifecycleManager extends BaseLifecycleManager { * mode (i.e. without any negotiation) then an instance of VideoStreamingParams is * returned. If the service was not opened then null is returned. */ - private VideoStreamingParameters tryStartVideoStream(boolean isEncrypted, VideoStreamingParameters parameters) { + private void tryStartVideoStream(boolean isEncrypted, VideoStreamingParameters parameters) { if (session == null) { DebugTool.logWarning(TAG, "SdlSession is not created yet."); - return null; + return; } if (getProtocolVersion() != null && getProtocolVersion().getMajor() >= 5 && !systemCapabilityManager.isCapabilitySupported(SystemCapabilityType.VIDEO_STREAMING)) { DebugTool.logWarning(TAG, "Module doesn't support video streaming."); - return null; + return; } if (parameters == null) { DebugTool.logWarning(TAG, "Video parameters were not supplied."); - return null; + return; } - if (!navServiceStartResponseReceived || !navServiceStartResponse //If we haven't started the service before - || (navServiceStartResponse && isEncrypted && !session.isServiceProtected(SessionType.NAV))) { //Or the service has been started but we'd like to start an encrypted one + + if (!videoServiceStartResponseReceived || !videoServiceStartResponse //If we haven't started the service before + || (videoServiceStartResponse && isEncrypted && !session.isServiceProtected(SessionType.NAV))) { //Or the service has been started but we'd like to start an encrypted one session.setDesiredVideoParams(parameters); - navServiceStartResponseReceived = false; - navServiceStartResponse = false; + videoServiceStartResponseReceived = false; + videoServiceStartResponse = false; + addVideoServiceListener(); session.startService(SessionType.NAV, session.getSessionId(), isEncrypted); - addNavListener(); - FutureTask<Void> fTask = new FutureTask<>(new CallableMethod(RESPONSE_WAIT_TIME)); - ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); - scheduler.execute(fTask); - - //noinspection StatementWithEmptyBody - while (!navServiceStartResponseReceived && !fTask.isDone()) ; - scheduler.shutdown(); - } - if (navServiceStartResponse) { - if (getProtocolVersion() != null && getProtocolVersion().getMajor() < 5) { //Versions 1-4 do not support streaming parameter negotiations - session.setAcceptedVideoParams(parameters); - } - return session.getAcceptedVideoParams(); } - - return null; } - private void addNavListener() { + private void addVideoServiceListener() { // videos may be started and stopped. Only add this once - if (navServiceListener == null) { + if (videoServiceListener == null) { - navServiceListener = new ISdlServiceListener() { + videoServiceListener = new ISdlServiceListener() { @Override public void onServiceStarted(SdlSession session, SessionType type, boolean isEncrypted) { + videoServiceStartResponseReceived = true; + videoServiceStartResponse = true; } @Override public void onServiceEnded(SdlSession session, SessionType type) { // reset nav flags so nav can start upon the next transport connection - navServiceStartResponseReceived = false; - navServiceStartResponse = false; + videoServiceStartResponseReceived = false; + videoServiceStartResponse = false; } @Override public void onServiceError(SdlSession session, SessionType type, String reason) { // if there is an error reset the flags so that there is a chance to restart streaming - navServiceStartResponseReceived = false; - navServiceStartResponse = false; + videoServiceStartResponseReceived = false; + videoServiceStartResponse = false; } }; - session.addServiceListener(SessionType.NAV, navServiceListener); + session.addServiceListener(SessionType.NAV, videoServiceListener); } } @@ -307,29 +267,17 @@ public class LifecycleManager extends BaseLifecycleManager { * @return true if the video service is closed successfully, return false otherwise */ @Override - boolean endVideoStream() { + void endVideoStream() { if (session == null) { DebugTool.logWarning(TAG, "SdlSession is not created yet."); - return false; + return; } if (!session.getIsConnected()) { DebugTool.logWarning(TAG, "Connection is not available."); - return false; + return; } - navServiceEndResponseReceived = false; - navServiceEndResponse = false; session.stopVideoStream(); - - FutureTask<Void> fTask = new FutureTask<>(new CallableMethod(RESPONSE_WAIT_TIME)); - ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); - scheduler.execute(fTask); - - //noinspection StatementWithEmptyBody - while (!navServiceEndResponseReceived && !fTask.isDone()) ; - scheduler.shutdown(); - - return navServiceEndResponse; } @Override @@ -351,46 +299,16 @@ public class LifecycleManager extends BaseLifecycleManager { * @return true if the audio service is closed successfully, return false otherwise */ @Override - boolean endAudioStream() { + void endAudioStream() { if (session == null) { DebugTool.logWarning(TAG, "SdlSession is not created yet."); - return false; + return; } if (!session.getIsConnected()) { DebugTool.logWarning(TAG, "Connection is not available."); - return false; + return; } - pcmServiceEndResponseReceived = false; - pcmServiceEndResponse = false; session.stopAudioStream(); - - FutureTask<Void> fTask = new FutureTask<>(new CallableMethod(RESPONSE_WAIT_TIME)); - ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); - scheduler.execute(fTask); - - //noinspection StatementWithEmptyBody - while (!pcmServiceEndResponseReceived && !fTask.isDone()) ; - scheduler.shutdown(); - - return pcmServiceEndResponse; - } - - private class CallableMethod implements Callable<Void> { - private final long waitTime; - - public CallableMethod(int timeInMillis) { - this.waitTime = timeInMillis; - } - - @Override - public Void call() { - try { - Thread.sleep(waitTime); - } catch (InterruptedException e) { - e.printStackTrace(); - } - return null; - } } } diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/SubscribeButtonManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/SubscribeButtonManager.java new file mode 100644 index 000000000..f38cf79d0 --- /dev/null +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/SubscribeButtonManager.java @@ -0,0 +1,17 @@ +package com.smartdevicelink.managers.screen; + +import android.support.annotation.NonNull; +import com.smartdevicelink.proxy.interfaces.ISdl; + +/** + * <strong>SubscribeButtonManager</strong> <br> + * + * Note: This class must be accessed through the SdlManager. Do not instantiate it by itself. <br> + * + */ +class SubscribeButtonManager extends BaseSubscribeButtonManager { + + SubscribeButtonManager(@NonNull ISdl internalInterface) { + super(internalInterface); + } +} diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportBroker.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportBroker.java index 68ac1e98b..becbdcbb4 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportBroker.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportBroker.java @@ -85,8 +85,7 @@ public class TransportBroker { private Context currentContext = null; private final Object INIT_LOCK = new Object(); - - private TransportType queuedOnTransportConnect = null; + private final Object MESSAGE_SEND_LOCK = new Object(); Messenger routerServiceMessenger = null; final Messenger clientMessenger; @@ -125,59 +124,55 @@ public class TransportBroker { }; } - protected synchronized boolean sendMessageToRouterService(Message message) { + protected boolean sendMessageToRouterService(Message message) { return sendMessageToRouterService(message, 0); } - protected synchronized boolean sendMessageToRouterService(Message message, int retryCount) { - if (message == null) { - DebugTool.logWarning(TAG, "Attempted to send null message"); - return false; - } - //Log.i(TAG, "Attempting to send message type - " + message.what); - if (isBound && routerServiceMessenger != null) { - if (registeredWithRouterService - || message.what == TransportConstants.ROUTER_REGISTER_CLIENT) { //We can send a message if we are registered or are attempting to register - try { - routerServiceMessenger.send(message); - return true; - } catch (RemoteException e) { - e.printStackTrace(); - //Let's check to see if we should retry - if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1 && e instanceof TransactionTooLargeException ) - || (retryCount < 5 && routerServiceMessenger.getBinder().isBinderAlive() && routerServiceMessenger.getBinder().pingBinder())) { //We probably just failed on a small transaction =\ - try { - Thread.sleep(100); - } catch (InterruptedException e1) { - e1.printStackTrace(); + protected boolean sendMessageToRouterService(Message message, int retryCount) { + synchronized (MESSAGE_SEND_LOCK) { + if (message == null) { + DebugTool.logWarning(TAG, "Attempted to send null message"); + return false; + } + //Log.i(TAG, "Attempting to send message type - " + message.what); + if (isBound && routerServiceMessenger != null && routerServiceMessenger.getBinder() != null && routerServiceMessenger.getBinder().isBinderAlive()) { + if (registeredWithRouterService + || message.what == TransportConstants.ROUTER_REGISTER_CLIENT) { //We can send a message if we are registered or are attempting to register + try { + routerServiceMessenger.send(message); + return true; + } catch (RemoteException e) { + e.printStackTrace(); + //Let's check to see if we should retry + if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1 && e instanceof TransactionTooLargeException) + || (retryCount < 5 && routerServiceMessenger.getBinder().isBinderAlive() && routerServiceMessenger.getBinder().pingBinder())) { //We probably just failed on a small transaction =\ + try { + Thread.sleep(100); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + return sendMessageToRouterService(message, retryCount++); + } else { + //DeadObject, time to kill our connection + DebugTool.logInfo(TAG, "Dead object while attempting to send packet"); + stop(); + onHardwareDisconnected(null, null); + return false; } - return sendMessageToRouterService(message, retryCount++); - } else { - //DeadObject, time to kill our connection - DebugTool.logInfo(TAG, "Dead object while attempting to send packet"); - routerServiceMessenger = null; - registeredWithRouterService = false; - unBindFromRouterService(); - isBound = false; + } catch (NullPointerException e) { + DebugTool.logInfo(TAG, "Null messenger while attempting to send packet"); // NPE, routerServiceMessenger is null + stop(); onHardwareDisconnected(null, null); return false; } - } catch (NullPointerException e) { - DebugTool.logInfo(TAG, "Null messenger while attempting to send packet"); // NPE, routerServiceMessenger is null - routerServiceMessenger = null; - registeredWithRouterService = false; - unBindFromRouterService(); - isBound = false; - onHardwareDisconnected(null, null); + } else { + DebugTool.logError(TAG, "Unable to send message to router service. Not registered."); return false; } } else { - DebugTool.logError(TAG, "Unable to send message to router service. Not registered."); + DebugTool.logError(TAG, "Unable to send message to router service. Not bound."); return false; } - } else { - DebugTool.logError(TAG, "Unable to send message to router service. Not bound."); - return false; } } @@ -415,7 +410,6 @@ public class TransportBroker { } //this.appId = appId.concat(timeStamp); this.appId = appId; - queuedOnTransportConnect = null; currentContext = context; //Log.d(TAG, "Registering our reply receiver: " + whereToReply); this.routerService = service; @@ -447,7 +441,6 @@ public class TransportBroker { synchronized (INIT_LOCK) { unregisterWithRouterService(); routerServiceMessenger = null; - queuedOnTransportConnect = null; unBindFromRouterService(); isBound = false; } @@ -462,13 +455,12 @@ public class TransportBroker { unregisterWithRouterService(); unBindFromRouterService(); routerServiceMessenger = null; - queuedOnTransportConnect = null; currentContext = null; } } - private synchronized void unBindFromRouterService() { + private void unBindFromRouterService() { try { getContext().unbindService(routerConnection); @@ -486,27 +478,17 @@ public class TransportBroker { public void onServiceUnregsiteredFromRouterService(int unregisterCode) { - queuedOnTransportConnect = null; } @Deprecated public void onHardwareDisconnected(TransportType type) { - routerServiceDisconnect(); + stop(); } public void onHardwareDisconnected(TransportRecord record, List<TransportRecord> connectedTransports) { } - private void routerServiceDisconnect() { - synchronized (INIT_LOCK) { - unBindFromRouterService(); - routerServiceMessenger = null; - routerConnection = null; - queuedOnTransportConnect = null; - } - } - /** * WILL NO LONGER BE CALLED * @@ -517,7 +499,6 @@ public class TransportBroker { public boolean onHardwareConnected(TransportType type) { synchronized (INIT_LOCK) { if (routerServiceMessenger == null) { - queuedOnTransportConnect = type; return false; } return true; @@ -527,7 +508,6 @@ public class TransportBroker { public boolean onHardwareConnected(List<TransportRecord> transports) { synchronized (INIT_LOCK) { if (routerServiceMessenger == null && transports != null && transports.size() > 0) { - queuedOnTransportConnect = transports.get(transports.size() - 1).getType(); return false; } return true; @@ -615,7 +595,7 @@ public class TransportBroker { ByteArrayMessageSpliter splitter = new ByteArrayMessageSpliter(appId, TransportConstants.ROUTER_SEND_PACKET, bytes, packet.getPrioirtyCoefficient()); splitter.setRouterServiceVersion(routerServiceVersion); splitter.setTransportRecord(packet.getTransportRecord()); - while (splitter.isActive()) { + while (splitter.isActive() && routerServiceMessenger != null) { sendMessageToRouterService(splitter.nextMessage()); } return splitter.close(); diff --git a/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java b/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java index 9e8eeb276..f362f990c 100644 --- a/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java @@ -82,7 +82,7 @@ abstract class BaseSdlManager { static final String TAG = "BaseSubManager"; final Object STATE_LOCK = new Object(); int state = -1; - String appId, appName, shortAppName; + String appId, appName, shortAppName, resumeHash; boolean isMediaApp; Language hmiLanguage; Language language; @@ -120,7 +120,7 @@ abstract class BaseSdlManager { // Initialize with anonymous lifecycleListener final LifecycleManager.LifecycleListener lifecycleListener = new LifecycleManager.LifecycleListener() { @Override - public void onProxyConnected(LifecycleManager lifeCycleManager) { + public void onConnected(LifecycleManager lifeCycleManager) { DebugTool.logInfo(TAG, "Proxy is connected. Now initializing."); synchronized (this) { changeRegistrationRetry = 0; @@ -140,7 +140,7 @@ abstract class BaseSdlManager { } @Override - public void onProxyClosed(LifecycleManager lifeCycleManager, String info, Exception e, SdlDisconnectedReason reason) { + public void onClosed(LifecycleManager lifeCycleManager, String info, Exception e, SdlDisconnectedReason reason) { DebugTool.logInfo(TAG, "Proxy is closed."); if (reason == null || !reason.equals(SdlDisconnectedReason.LANGUAGE_CHANGE)) { dispose(); @@ -332,6 +332,7 @@ abstract class BaseSdlManager { appConfig.setAppID(appId); appConfig.setMinimumProtocolVersion(minimumProtocolVersion); appConfig.setMinimumRPCVersion(minimumRPCVersion); + appConfig.setResumeHash(resumeHash); lifecycleManager = new LifecycleManager(appConfig, transport, lifecycleListener); _internalInterface = lifecycleManager.getInternalInterface((SdlManager) BaseSdlManager.this); @@ -629,6 +630,16 @@ abstract class BaseSdlManager { } /** + * Sets the Resumption Hash ID + * + * @param resumeHash String representation of the Hash ID Used to resume the application + */ + public Builder setResumeHash(final String resumeHash) { + sdlManager.resumeHash = resumeHash; + return this; + } + + /** * Sets the Application Name * * @param appName String that will be associated as the app's name diff --git a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java index 0497676da..4a0d0f096 100644 --- a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java @@ -142,7 +142,7 @@ abstract class BaseLifecycleManager { this.lifecycleListener = listener; this.minimumProtocolVersion = appConfig.getMinimumProtocolVersion(); this.minimumRPCVersion = appConfig.getMinimumRPCVersion(); - initializeProxy(); + initialize(); } public void start() { @@ -162,8 +162,11 @@ abstract class BaseLifecycleManager { } } - public void stop() { - session.close(); + public synchronized void stop() { + if(session != null) { + session.close(); + session = null; + } if (taskmaster != null) { taskmaster.shutdown(); } @@ -173,7 +176,7 @@ abstract class BaseLifecycleManager { if (taskmaster == null) { Taskmaster.Builder builder = new Taskmaster.Builder(); builder.setThreadCount(2); - builder.shouldBeDaemon(false); + builder.shouldBeDaemon(true); taskmaster = builder.build(); taskmaster.start(); } @@ -342,9 +345,9 @@ abstract class BaseLifecycleManager { } void onClose(String info, Exception e, SdlDisconnectedReason reason) { - DebugTool.logInfo(TAG, "onClose"); - if (lifecycleListener != null) { - lifecycleListener.onProxyClosed((LifecycleManager) this, info, e, reason); + DebugTool.logInfo(TAG, "onClose"); + if (lifecycleListener != null) { + lifecycleListener.onClosed((LifecycleManager) this, info, e, reason); } } @@ -398,7 +401,7 @@ abstract class BaseLifecycleManager { UnregisterAppInterface msg = new UnregisterAppInterface(); msg.setCorrelationID(UNREGISTER_APP_INTERFACE_CORRELATION_ID); sendRPCMessagePrivate(msg, true); - cleanProxy(); + clean(); return; } processRaiResponse(raiResponse); @@ -409,7 +412,7 @@ abstract class BaseLifecycleManager { boolean shouldInit = currentHMIStatus == null; currentHMIStatus = (OnHMIStatus) message; if (lifecycleListener != null && shouldInit) { - lifecycleListener.onProxyConnected((LifecycleManager) BaseLifecycleManager.this); + lifecycleListener.onConnected((LifecycleManager) BaseLifecycleManager.this); } break; case ON_HASH_CHANGE: @@ -458,15 +461,15 @@ abstract class BaseLifecycleManager { if (!onAppInterfaceUnregistered.getReason().equals(AppInterfaceUnregisteredReason.LANGUAGE_CHANGE)) { DebugTool.logInfo(TAG, "on app interface unregistered"); - cleanProxy(); + clean(); } else { DebugTool.logInfo(TAG, "re-registering for language change"); - cycleProxy(SdlDisconnectedReason.LANGUAGE_CHANGE); + cycle(SdlDisconnectedReason.LANGUAGE_CHANGE); } break; case UNREGISTER_APP_INTERFACE: DebugTool.logInfo(TAG, "unregister app interface"); - cleanProxy(); + clean(); break; } } @@ -670,7 +673,7 @@ abstract class BaseLifecycleManager { @SuppressWarnings("UnusedReturnValue") private boolean onRPCRequestReceived(RPCRequest request) { if (request == null) { - DebugTool.logError(TAG, "onRPCRequestReceived - request was null"); + DebugTool.logError("onRPCRequestReceived - request was null"); return false; } DebugTool.logInfo(TAG, "onRPCRequestReceived - " + request.getFunctionName()); @@ -918,24 +921,24 @@ abstract class BaseLifecycleManager { @Override public void onProtocolSessionStartedNACKed(SessionType sessionType, byte sessionID, byte version, String correlationID, List<String> rejectedParams) { - DebugTool.logWarning(TAG, sessionType + " onProtocolSessionStartedNACKed " + sessionID + " RejectedParams: " + rejectedParams); - BaseLifecycleManager.this.onProtocolSessionStartedNACKed(sessionType); + DebugTool.logWarning(TAG, sessionType.getName() + " onProtocolSessionStartedNACKed " + sessionID + " RejectedParams: " + rejectedParams); + BaseLifecycleManager.this.onStartServiceNACKed(sessionType); } @Override public void onProtocolSessionStarted(SessionType sessionType, byte sessionID, byte version, String correlationID, int hashID, boolean isEncrypted) { DebugTool.logInfo(TAG, "on protocol session started"); - BaseLifecycleManager.this.onProtocolSessionStarted(sessionType); + BaseLifecycleManager.this.onServiceStarted(sessionType); } @Override public void onProtocolSessionEnded(SessionType sessionType, byte sessionID, String correlationID) { - BaseLifecycleManager.this.onProtocolSessionEnded(sessionType); + //Currently not necessary } @Override public void onProtocolSessionEndedNACKed(SessionType sessionType, byte sessionID, String correlationID) { - BaseLifecycleManager.this.onProtocolSessionEndedNACKed(sessionType); + //Currently not necessary } @Override @@ -1172,166 +1175,6 @@ abstract class BaseLifecycleManager { ********************************************* ISdl - END ************************************************ *********************************************************************************************************/ - public interface LifecycleListener { - void onProxyConnected(LifecycleManager lifeCycleManager); - - void onProxyClosed(LifecycleManager lifeCycleManager, String info, Exception e, SdlDisconnectedReason reason); - - void onServiceStarted(SessionType sessionType); - - void onServiceEnded(SessionType sessionType); - - void onError(LifecycleManager lifeCycleManager, String info, Exception e); - } - - public static class AppConfig { - private String appID, appName, ngnMediaScreenAppName; - private Vector<TTSChunk> ttsName; - private Vector<String> vrSynonyms; - private boolean isMediaApp = false; - private Language languageDesired, hmiDisplayLanguageDesired; - private Vector<AppHMIType> appType; - private TemplateColorScheme dayColorScheme, nightColorScheme; - private Version minimumProtocolVersion; - private Version minimumRPCVersion; - - private void prepare() { - if (getNgnMediaScreenAppName() == null) { - setNgnMediaScreenAppName(getAppName()); - } - - if (getLanguageDesired() == null) { - setLanguageDesired(Language.EN_US); - } - - if (getHmiDisplayLanguageDesired() == null) { - setHmiDisplayLanguageDesired(Language.EN_US); - } - - if (getVrSynonyms() == null) { - setVrSynonyms(new Vector<String>()); - getVrSynonyms().add(getAppName()); - } - } - - public String getAppID() { - return appID; - } - - public void setAppID(String appID) { - this.appID = appID; - } - - public String getAppName() { - return appName; - } - - public void setAppName(String appName) { - this.appName = appName; - } - - public String getNgnMediaScreenAppName() { - return ngnMediaScreenAppName; - } - - public void setNgnMediaScreenAppName(String ngnMediaScreenAppName) { - this.ngnMediaScreenAppName = ngnMediaScreenAppName; - } - - public Vector<TTSChunk> getTtsName() { - return ttsName; - } - - public void setTtsName(Vector<TTSChunk> ttsName) { - this.ttsName = ttsName; - } - - public Vector<String> getVrSynonyms() { - return vrSynonyms; - } - - public void setVrSynonyms(Vector<String> vrSynonyms) { - this.vrSynonyms = vrSynonyms; - } - - public boolean isMediaApp() { - return isMediaApp; - } - - public void setMediaApp(boolean mediaApp) { - isMediaApp = mediaApp; - } - - public Language getLanguageDesired() { - return languageDesired; - } - - public void setLanguageDesired(Language languageDesired) { - this.languageDesired = languageDesired; - } - - public Language getHmiDisplayLanguageDesired() { - return hmiDisplayLanguageDesired; - } - - public void setHmiDisplayLanguageDesired(Language hmiDisplayLanguageDesired) { - this.hmiDisplayLanguageDesired = hmiDisplayLanguageDesired; - } - - public Vector<AppHMIType> getAppType() { - return appType; - } - - public void setAppType(Vector<AppHMIType> appType) { - this.appType = appType; - } - - public TemplateColorScheme getDayColorScheme() { - return dayColorScheme; - } - - public void setDayColorScheme(TemplateColorScheme dayColorScheme) { - this.dayColorScheme = dayColorScheme; - } - - public TemplateColorScheme getNightColorScheme() { - return nightColorScheme; - } - - public void setNightColorScheme(TemplateColorScheme nightColorScheme) { - this.nightColorScheme = nightColorScheme; - } - - public Version getMinimumProtocolVersion() { - return minimumProtocolVersion; - } - - /** - * 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 a Version object with the minimally accepted Protocol version - */ - public void setMinimumProtocolVersion(Version minimumProtocolVersion) { - this.minimumProtocolVersion = minimumProtocolVersion; - } - - public Version getMinimumRPCVersion() { - return minimumRPCVersion; - } - - /** - * 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 a Version object with the minimally accepted RPC spec version - */ - public void setMinimumRPCVersion(Version minimumRPCVersion) { - this.minimumRPCVersion = minimumRPCVersion; - } - } - /** * Temporary method to bridge the new PLAY_PAUSE and OKAY button functionality with the old * OK button name. This should be removed during the next major release @@ -1385,7 +1228,7 @@ abstract class BaseLifecycleManager { return null; } - void cleanProxy() { + void clean() { firstTimeFull = true; currentHMIStatus = null; if (rpcListeners != null) { @@ -1465,7 +1308,7 @@ abstract class BaseLifecycleManager { ********************************** Platform specific methods - START ************************************* *********************************************************************************************************/ - void initializeProxy() { + void initialize() { this.rpcListeners = new HashMap<>(); this.rpcResponseListeners = new HashMap<>(); this.rpcNotificationListeners = new HashMap<>(); @@ -1474,12 +1317,12 @@ abstract class BaseLifecycleManager { setupInternalRpcListeners(); } - void onProtocolSessionStarted(SessionType sessionType) { + void onServiceStarted(SessionType sessionType) { if (sessionType != null) { if (minimumProtocolVersion != null && minimumProtocolVersion.isNewerThan(getProtocolVersion()) == 1) { DebugTool.logWarning(TAG, String.format("Disconnecting from head unit, the configured minimum protocol version %s is greater than the supported protocol version %s", minimumProtocolVersion, getProtocolVersion())); session.endService(sessionType, session.getSessionId()); - cleanProxy(); + clean(); return; } @@ -1504,9 +1347,9 @@ abstract class BaseLifecycleManager { rai.setAppHMIType(appConfig.getAppType()); rai.setDayColorScheme(appConfig.getDayColorScheme()); rai.setNightColorScheme(appConfig.getNightColorScheme()); + rai.setHashID(appConfig.getResumeHash()); //Add device/system info in the future - //TODO attach previous hash id sendRPCMessagePrivate(rai, true); } else { @@ -1516,32 +1359,25 @@ abstract class BaseLifecycleManager { } } - abstract void cycleProxy(SdlDisconnectedReason disconnectedReason); + abstract void cycle(SdlDisconnectedReason disconnectedReason); void onTransportDisconnected(String info, boolean availablePrimary, BaseTransportConfig transportConfig) { } - void onProtocolSessionStartedNACKed(SessionType sessionType) { - } - - void onProtocolSessionEnded(SessionType sessionType) { + void onStartServiceNACKed(SessionType sessionType) { } - void onProtocolSessionEndedNACKed(SessionType sessionType) { - } void startVideoService(boolean encrypted, VideoStreamingParameters parameters) { } - boolean endVideoStream() { - return false; + void endVideoStream() { } void startAudioService(boolean encrypted) { } - boolean endAudioStream() { - return false; + void endAudioStream() { } void setSdlSecurityStaticVars() { @@ -1550,4 +1386,180 @@ abstract class BaseLifecycleManager { /* ******************************************************************************************************* ********************************** Platform specific methods - End ************************************* *********************************************************************************************************/ + + /* ******************************************************************************************************* + ****************************** Inner Classes and Interfaces - Start ************************************* + *********************************************************************************************************/ + + public interface LifecycleListener { + void onConnected(LifecycleManager lifeCycleManager); + + void onClosed(LifecycleManager lifeCycleManager, String info, Exception e, SdlDisconnectedReason reason); + + void onServiceStarted(SessionType sessionType); + + void onServiceEnded(SessionType sessionType); + + void onError(LifecycleManager lifeCycleManager, String info, Exception e); + } + + public static class AppConfig { + private String appID, appName, ngnMediaScreenAppName, resumeHash; + private Vector<TTSChunk> ttsName; + private Vector<String> vrSynonyms; + private boolean isMediaApp = false; + private Language languageDesired, hmiDisplayLanguageDesired; + private Vector<AppHMIType> appType; + private TemplateColorScheme dayColorScheme, nightColorScheme; + private Version minimumProtocolVersion; + private Version minimumRPCVersion; + + private void prepare() { + if (getNgnMediaScreenAppName() == null) { + setNgnMediaScreenAppName(getAppName()); + } + + if (getLanguageDesired() == null) { + setLanguageDesired(Language.EN_US); + } + + if (getHmiDisplayLanguageDesired() == null) { + setHmiDisplayLanguageDesired(Language.EN_US); + } + + if (getVrSynonyms() == null) { + setVrSynonyms(new Vector<String>()); + getVrSynonyms().add(getAppName()); + } + } + + public String getAppID() { + return appID; + } + + public void setAppID(String appID) { + this.appID = appID; + } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public String getNgnMediaScreenAppName() { + return ngnMediaScreenAppName; + } + + public void setNgnMediaScreenAppName(String ngnMediaScreenAppName) { + this.ngnMediaScreenAppName = ngnMediaScreenAppName; + } + + public Vector<TTSChunk> getTtsName() { + return ttsName; + } + + public void setTtsName(Vector<TTSChunk> ttsName) { + this.ttsName = ttsName; + } + + public Vector<String> getVrSynonyms() { + return vrSynonyms; + } + + public void setVrSynonyms(Vector<String> vrSynonyms) { + this.vrSynonyms = vrSynonyms; + } + + public boolean isMediaApp() { + return isMediaApp; + } + + public void setMediaApp(boolean mediaApp) { + isMediaApp = mediaApp; + } + + public Language getLanguageDesired() { + return languageDesired; + } + + public void setLanguageDesired(Language languageDesired) { + this.languageDesired = languageDesired; + } + + public Language getHmiDisplayLanguageDesired() { + return hmiDisplayLanguageDesired; + } + + public void setHmiDisplayLanguageDesired(Language hmiDisplayLanguageDesired) { + this.hmiDisplayLanguageDesired = hmiDisplayLanguageDesired; + } + + public Vector<AppHMIType> getAppType() { + return appType; + } + + public void setAppType(Vector<AppHMIType> appType) { + this.appType = appType; + } + + public String getResumeHash() { + return this.resumeHash; + } + + public void setResumeHash(String resumeHash) { + this.resumeHash = resumeHash; + } + + public TemplateColorScheme getDayColorScheme() { + return dayColorScheme; + } + + public void setDayColorScheme(TemplateColorScheme dayColorScheme) { + this.dayColorScheme = dayColorScheme; + } + + public TemplateColorScheme getNightColorScheme() { + return nightColorScheme; + } + + public void setNightColorScheme(TemplateColorScheme nightColorScheme) { + this.nightColorScheme = nightColorScheme; + } + + public Version getMinimumProtocolVersion() { + return minimumProtocolVersion; + } + + /** + * 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 a Version object with the minimally accepted Protocol version + */ + public void setMinimumProtocolVersion(Version minimumProtocolVersion) { + this.minimumProtocolVersion = minimumProtocolVersion; + } + + public Version getMinimumRPCVersion() { + return minimumRPCVersion; + } + + /** + * 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 a Version object with the minimally accepted RPC spec version + */ + public void setMinimumRPCVersion(Version minimumRPCVersion) { + this.minimumRPCVersion = minimumRPCVersion; + } + } + + /* ******************************************************************************************************* + ****************************** Inner Classes and Interfaces - End *************************************** + *********************************************************************************************************/ } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java index a1b94daaa..7b6ee1c30 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java @@ -50,6 +50,7 @@ import com.smartdevicelink.managers.screen.menu.VoiceCommand; import com.smartdevicelink.managers.screen.menu.VoiceCommandManager; import com.smartdevicelink.proxy.interfaces.ISdl; import com.smartdevicelink.proxy.rpc.KeyboardProperties; +import com.smartdevicelink.proxy.rpc.enums.ButtonName; import com.smartdevicelink.proxy.rpc.enums.InteractionMode; import com.smartdevicelink.proxy.rpc.enums.MetadataType; import com.smartdevicelink.proxy.rpc.enums.TextAlignment; @@ -73,19 +74,23 @@ abstract class BaseScreenManager extends BaseSubManager { private VoiceCommandManager voiceCommandManager; private MenuManager menuManager; private ChoiceSetManager choiceSetManager; + private SubscribeButtonManager subscribeButtonManager; // Sub manager listener private final CompletionListener subManagerListener = new CompletionListener() { @Override public synchronized void onComplete(boolean success) { - if (softButtonManager != null && textAndGraphicManager != null && voiceCommandManager != null && menuManager != null && choiceSetManager != null) { - if (softButtonManager.getState() == BaseSubManager.READY && textAndGraphicManager.getState() == BaseSubManager.READY && voiceCommandManager.getState() == BaseSubManager.READY && menuManager.getState() == BaseSubManager.READY) { + if (softButtonManager != null && textAndGraphicManager != null && voiceCommandManager != null && menuManager != null && choiceSetManager != null && subscribeButtonManager != null) { + if (softButtonManager.getState() == BaseSubManager.READY && textAndGraphicManager.getState() == BaseSubManager.READY && voiceCommandManager.getState() == BaseSubManager.READY && menuManager.getState() == BaseSubManager.READY + && subscribeButtonManager.getState() == BaseSubManager.READY) { DebugTool.logInfo(TAG, "Starting screen manager, all sub managers are in ready state"); transitionToState(READY); - } else if (softButtonManager.getState() == BaseSubManager.ERROR && textAndGraphicManager.getState() == BaseSubManager.ERROR && voiceCommandManager.getState() == BaseSubManager.ERROR && menuManager.getState() == BaseSubManager.ERROR && choiceSetManager.getState() == BaseSubManager.ERROR) { + } else if (softButtonManager.getState() == BaseSubManager.ERROR && textAndGraphicManager.getState() == BaseSubManager.ERROR && voiceCommandManager.getState() == BaseSubManager.ERROR && menuManager.getState() == BaseSubManager.ERROR + && choiceSetManager.getState() == BaseSubManager.ERROR && subscribeButtonManager.getState() == BaseSubManager.ERROR) { DebugTool.logError(TAG, "ERROR starting screen manager, all sub managers are in error state"); transitionToState(ERROR); - } else if (textAndGraphicManager.getState() == BaseSubManager.SETTING_UP || softButtonManager.getState() == BaseSubManager.SETTING_UP || voiceCommandManager.getState() == BaseSubManager.SETTING_UP || menuManager.getState() == BaseSubManager.SETTING_UP || choiceSetManager.getState() == BaseSubManager.SETTING_UP) { + } else if (textAndGraphicManager.getState() == BaseSubManager.SETTING_UP || softButtonManager.getState() == BaseSubManager.SETTING_UP || voiceCommandManager.getState() == BaseSubManager.SETTING_UP || menuManager.getState() == BaseSubManager.SETTING_UP + || choiceSetManager.getState() == BaseSubManager.SETTING_UP || subscribeButtonManager.getState() == BaseSubManager.SETTING_UP) { DebugTool.logInfo(TAG, "SETTING UP screen manager, at least one sub manager is still setting up"); transitionToState(SETTING_UP); } else { @@ -114,6 +119,7 @@ abstract class BaseScreenManager extends BaseSubManager { this.voiceCommandManager.start(subManagerListener); this.menuManager.start(subManagerListener); this.choiceSetManager.start(subManagerListener); + this.subscribeButtonManager.start(subManagerListener); } private void initialize(){ @@ -123,6 +129,7 @@ abstract class BaseScreenManager extends BaseSubManager { this.menuManager = new MenuManager(internalInterface, fileManager.get()); this.choiceSetManager = new ChoiceSetManager(internalInterface, fileManager.get()); } + this.subscribeButtonManager = new SubscribeButtonManager(internalInterface); this.voiceCommandManager = new VoiceCommandManager(internalInterface); } @@ -136,6 +143,7 @@ abstract class BaseScreenManager extends BaseSubManager { voiceCommandManager.dispose(); menuManager.dispose(); choiceSetManager.dispose(); + subscribeButtonManager.dispose(); super.dispose(); } @@ -577,4 +585,11 @@ abstract class BaseScreenManager extends BaseSubManager { } }); } + + public void addButtonListener(@NonNull ButtonName buttonName, @NonNull OnButtonListener listener){ + subscribeButtonManager.addButtonListener(buttonName,listener); + } + public void removeButtonListener(@NonNull ButtonName buttonName, @NonNull OnButtonListener listener){ + subscribeButtonManager.removeButtonListener(buttonName, listener); + } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/BaseSubscribeButtonManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/BaseSubscribeButtonManager.java new file mode 100644 index 000000000..70996db4c --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/managers/screen/BaseSubscribeButtonManager.java @@ -0,0 +1,193 @@ +package com.smartdevicelink.managers.screen; + +import android.support.annotation.NonNull; +import android.util.Log; + +import com.smartdevicelink.managers.BaseSubManager; +import com.smartdevicelink.managers.CompletionListener; +import com.smartdevicelink.protocol.enums.FunctionID; +import com.smartdevicelink.proxy.RPCNotification; +import com.smartdevicelink.proxy.RPCResponse; +import com.smartdevicelink.proxy.interfaces.ISdl; +import com.smartdevicelink.proxy.rpc.OnButtonEvent; +import com.smartdevicelink.proxy.rpc.OnButtonPress; +import com.smartdevicelink.proxy.rpc.SubscribeButton; +import com.smartdevicelink.proxy.rpc.UnsubscribeButton; +import com.smartdevicelink.proxy.rpc.enums.ButtonName; +import com.smartdevicelink.proxy.rpc.enums.Result; +import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener; +import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener; + +import java.util.HashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * <strong>SubscribeButtonManager</strong> <br> + * <p> + * Note: This class must be accessed through the SdlManager. Do not instantiate it by itself. <br> + */ +abstract class BaseSubscribeButtonManager extends BaseSubManager { + + private static final String TAG = "SubscribeButtonManager"; + HashMap<ButtonName, CopyOnWriteArrayList<OnButtonListener>> onButtonListeners; + private OnRPCNotificationListener onButtonPressListener; + private OnRPCNotificationListener onButtonEventListener; + + BaseSubscribeButtonManager(@NonNull ISdl internalInterface) { + super(internalInterface); + setRpcNotificationListeners(); + onButtonListeners = new HashMap<>(); + } + + @Override + public void start(CompletionListener listener) { + transitionToState(READY); + super.start(listener); + } + + @Override + public void dispose() { + super.dispose(); + if (onButtonListeners != null) { + onButtonListeners.clear(); + } + internalInterface.removeOnRPCNotificationListener(FunctionID.ON_BUTTON_PRESS, onButtonPressListener); + internalInterface.removeOnRPCNotificationListener(FunctionID.ON_BUTTON_EVENT, onButtonEventListener); + } + + /*** + * Checks to see if Button is already subscribed and adds listener to hashmap. + * If button is not already subscribed to, it call method: + * subscribeButtonRequest to send RPC request + * @param buttonName - Is the button that the developer wants to subscribe to + * @param listener - Is the listener that was sent by developer + */ + void addButtonListener(ButtonName buttonName, OnButtonListener listener) { + if (listener == null) { + Log.e(TAG, "OnButtonListener cannot be null"); + return; + } + if (buttonName == null) { + listener.onError("ButtonName cannot be null"); + return; + } + + if (onButtonListeners.get(buttonName) == null) { + subscribeButtonRequest(buttonName, listener); + return; + } + + if (onButtonListeners.get(buttonName).contains(listener)) { + Log.w(TAG, "Already subscribed to button named: " + buttonName); + return; + } + onButtonListeners.get(buttonName).add(listener); + } + + /** + * Unsubscribe form button and/or listener sent by developer + * + * @param buttonName Is the button that the developer wants to unsubscribe from + * @param listener - the listener that was sent by developer + */ + void removeButtonListener(final ButtonName buttonName, final OnButtonListener listener) { + if (listener == null) { + Log.e(TAG, "OnButtonListener cannot be null: "); + return; + } + + if (buttonName == null) { + listener.onError("ButtonName cannot be null"); + return; + } + + if (onButtonListeners.get(buttonName) == null || !onButtonListeners.get(buttonName).contains(listener)) { + listener.onError("Attempting to unsubscribe to the " + buttonName + " button failed because it is not currently subscribed"); + return; + } + + if (onButtonListeners.get(buttonName).size() > 1) { + onButtonListeners.get(buttonName).remove(listener); + return; + } + unsubscribeButtonRequest(buttonName, listener); + } + + /** + * Send the UnsubscribeButton RPC + * + * @param buttonName - ButtonName - name of button + * @param listener - OnButtonListener - listener to get notified + */ + private void unsubscribeButtonRequest(final ButtonName buttonName, final OnButtonListener listener) { + UnsubscribeButton unsubscribeButtonRequest = new UnsubscribeButton(buttonName); + unsubscribeButtonRequest.setOnRPCResponseListener(new OnRPCResponseListener() { + @Override + public void onResponse(int correlationId, RPCResponse response) { + onButtonListeners.remove(buttonName); + } + + @Override + public void onError(int correlationId, Result resultCode, String info) { + listener.onError("Attempt to unsubscribe to button named " + buttonName + " Failed. ResultCode: " + resultCode + " info: " + info); + } + }); + internalInterface.sendRPC(unsubscribeButtonRequest); + } + + /** + * Send the SubscribeButton RPC + * + * @param buttonName - ButtonName - name of button + * @param listener - OnButtonListener - listener to get notified + */ + private void subscribeButtonRequest(final ButtonName buttonName, final OnButtonListener listener) { + SubscribeButton subscribeButtonRequest = new SubscribeButton(buttonName); + subscribeButtonRequest.setOnRPCResponseListener(new OnRPCResponseListener() { + @Override + public void onResponse(int correlationId, RPCResponse response) { + onButtonListeners.put(buttonName, new CopyOnWriteArrayList<OnButtonListener>()); + onButtonListeners.get(buttonName).add(listener); + } + + @Override + public void onError(int correlationId, Result resultCode, String info) { + listener.onError("Attempt to subscribe to button named " + buttonName + " Failed . ResultCode: " + resultCode + " info: " + info); + } + }); + internalInterface.sendRPC(subscribeButtonRequest); + } + + /** + * Sets up RpcNotificationListeners for button presses and events, is setup when manager is created + */ + private void setRpcNotificationListeners() { + onButtonPressListener = new OnRPCNotificationListener() { + @Override + public void onNotified(RPCNotification notification) { + OnButtonPress onButtonPressNotification = (OnButtonPress) notification; + CopyOnWriteArrayList<OnButtonListener> listeners = onButtonListeners.get(onButtonPressNotification.getButtonName()); + if (listeners != null && listeners.size() > 0) { + for (OnButtonListener listener : listeners) { + listener.onPress(onButtonPressNotification.getButtonName(), onButtonPressNotification); + } + } + } + }; + internalInterface.addOnRPCNotificationListener(FunctionID.ON_BUTTON_PRESS, onButtonPressListener); + + onButtonEventListener = new OnRPCNotificationListener() { + @Override + public void onNotified(RPCNotification notification) { + OnButtonEvent onButtonEvent = (OnButtonEvent) notification; + CopyOnWriteArrayList<OnButtonListener> listeners = onButtonListeners.get(onButtonEvent.getButtonName()); + if (listeners != null && listeners.size() > 0) { + for (OnButtonListener listener : listeners) { + listener.onEvent(onButtonEvent.getButtonName(), onButtonEvent); + } + } + } + }; + internalInterface.addOnRPCNotificationListener(FunctionID.ON_BUTTON_EVENT, onButtonEventListener); + } +} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/OnButtonListener.java b/base/src/main/java/com/smartdevicelink/managers/screen/OnButtonListener.java new file mode 100644 index 000000000..ba2910307 --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/managers/screen/OnButtonListener.java @@ -0,0 +1,32 @@ +package com.smartdevicelink.managers.screen; + +import com.smartdevicelink.proxy.rpc.OnButtonEvent; +import com.smartdevicelink.proxy.rpc.OnButtonPress; +import com.smartdevicelink.proxy.rpc.enums.ButtonName; + +/** + * OnButtonListener is a listener used for notifying when events have happened with SubscribeButtons + */ +public interface OnButtonListener { + + /** + * Returns when a Subscribed button is pressed + * @param buttonName - Name of Button + * @param buttonPress - OnButtonPress + */ + void onPress(ButtonName buttonName, OnButtonPress buttonPress); + + /** + * Returns when a Subscribed button Event has occurred + * @param buttonName - Name of Button + * @param buttonEvent - OnButtonEvent + */ + void onEvent(ButtonName buttonName, OnButtonEvent buttonEvent); + + /** + * Returns when there is an error with subscribing to button + * @param info - Error info + */ + void onError(String info); +} + diff --git a/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java b/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java index 60c979d5a..d1f1e775b 100644 --- a/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java +++ b/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java @@ -33,6 +33,7 @@ package com.smartdevicelink.java; import com.smartdevicelink.managers.CompletionListener; +import com.smartdevicelink.managers.screen.OnButtonListener; import com.smartdevicelink.managers.SdlManager; import com.smartdevicelink.managers.SdlManagerListener; import com.smartdevicelink.managers.file.filetypes.SdlArtwork; @@ -75,15 +76,12 @@ public class SdlService { private static final String IMAGE_DIR = "assets/images/"; - - // variable to create and call functions of the SyncProxy private SdlManager sdlManager = null; private List<ChoiceCell> choiceCellList; private SdlServiceCallback callback; - public SdlService(BaseTransportConfig config, SdlServiceCallback callback){ this.callback = callback; buildSdlManager(config); @@ -191,6 +189,7 @@ public class SdlService { performWelcomeSpeak(); performWelcomeShow(); preloadChoices(); + subscribeToButtons(); } } }); @@ -327,6 +326,37 @@ public class SdlService { } /** + * Attempts to Subscribe to all preset buttons + */ + private void subscribeToButtons() { + ButtonName[] buttonNames = {ButtonName.PLAY_PAUSE, ButtonName.SEEKLEFT, ButtonName.SEEKRIGHT, ButtonName.AC_MAX, ButtonName.AC, ButtonName.RECIRCULATE, + ButtonName.FAN_UP, ButtonName.FAN_DOWN, ButtonName.TEMP_UP, ButtonName.TEMP_DOWN, ButtonName.FAN_DOWN, ButtonName.DEFROST_MAX, ButtonName.DEFROST_REAR, ButtonName.DEFROST, + ButtonName.UPPER_VENT, ButtonName.LOWER_VENT, ButtonName.VOLUME_UP, ButtonName.VOLUME_DOWN, ButtonName.EJECT, ButtonName.SOURCE, ButtonName.SHUFFLE, ButtonName.REPEAT}; + + OnButtonListener onButtonListener = new OnButtonListener() { + @Override + public void onPress(ButtonName buttonName, OnButtonPress buttonPress) { + sdlManager.getScreenManager().setTextField1(buttonName + " pressed"); + Log.i(TAG, "onPress: " + buttonName); + } + + @Override + public void onEvent(ButtonName buttonName, OnButtonEvent buttonEvent) { + sdlManager.getScreenManager().setTextField2(buttonName + " " + buttonEvent.getButtonEventMode()); + } + + @Override + public void onError(String info) { + Log.i(TAG, "onError: " + info); + } + }; + + for (ButtonName buttonName : buttonNames) { + sdlManager.getScreenManager().addButtonListener(buttonName, onButtonListener); + } + } + + /** * Will show a sample test message on screen as well as speak a sample test message */ private void showTest(){ diff --git a/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java b/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java index b249d1223..4cfadf611 100644 --- a/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java +++ b/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java @@ -51,14 +51,14 @@ public class LifecycleManager extends BaseLifecycleManager { } @Override - void initializeProxy() { - super.initializeProxy(); + void initialize() { + super.initialize(); this.session = new SdlSession(sdlConnectionListener, _transportConfig); } @Override - void cycleProxy(SdlDisconnectedReason disconnectedReason) { - cleanProxy(); + void cycle(SdlDisconnectedReason disconnectedReason) { + clean(); if (session != null) { try { session.startSession(); diff --git a/javaSE/src/main/java/com/smartdevicelink/managers/screen/SubscribeButtonManager.java b/javaSE/src/main/java/com/smartdevicelink/managers/screen/SubscribeButtonManager.java new file mode 100644 index 000000000..c34b0775d --- /dev/null +++ b/javaSE/src/main/java/com/smartdevicelink/managers/screen/SubscribeButtonManager.java @@ -0,0 +1,11 @@ +package com.smartdevicelink.managers.screen; + +import android.support.annotation.NonNull; +import com.smartdevicelink.proxy.interfaces.ISdl; + +public class SubscribeButtonManager extends BaseSubscribeButtonManager { + + public SubscribeButtonManager(@NonNull ISdl internalInterface) { + super(internalInterface); + } +} |