diff options
author | RHenigan <heniganr1@gmail.com> | 2020-06-30 12:59:31 -0400 |
---|---|---|
committer | RHenigan <heniganr1@gmail.com> | 2020-06-30 12:59:31 -0400 |
commit | a6521989f44b6854ebaa7184c6bf8faeccf48782 (patch) | |
tree | 2467e2cb9e8ec930a3ee107b3b8c8f62f686e4e7 | |
parent | d679c2ce887cb5c3f1eb2fca6a9695687ea9ab0f (diff) | |
parent | 3a2800d55a150510749e2758ef72c95b7ecc5640 (diff) | |
download | sdl_android-a6521989f44b6854ebaa7184c6bf8faeccf48782.tar.gz |
Merge branch 'develop' into bugfix/issue_1400
14 files changed, 800 insertions, 407 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 89f2edc4e..b993724ba 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 @@ -216,7 +216,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 fde161b80..38c8e4c26 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 @@ -54,38 +54,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())) { @@ -113,9 +104,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); @@ -131,14 +122,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; } @@ -147,11 +143,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; } } @@ -161,42 +157,18 @@ public class LifecycleManager extends BaseLifecycleManager { if (availablePrimary) { _transportConfig = transportConfig; Log.d(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; } } @@ -233,72 +205,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("SdlSession is not created yet."); - return null; + return; } if (getProtocolVersion() != null && getProtocolVersion().getMajor() >= 5 && !systemCapabilityManager.isCapabilitySupported(SystemCapabilityType.VIDEO_STREAMING)) { DebugTool.logWarning("Module doesn't support video streaming."); - return null; + return; } if (parameters == null) { DebugTool.logWarning("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); } } @@ -308,29 +268,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("SdlSession is not created yet."); - return false; + return; } if (!session.getIsConnected()) { DebugTool.logWarning("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 @@ -352,46 +300,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("SdlSession is not created yet."); - return false; + return; } if (!session.getIsConnected()) { DebugTool.logWarning("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 710e9d610..003556109 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 @@ -86,8 +86,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; @@ -126,59 +125,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) { - Log.w(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) { + Log.w(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 + Log.d(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 - Log.d(TAG, "Dead object while attempting to send packet"); - routerServiceMessenger = null; - registeredWithRouterService = false; - unBindFromRouterService(); - isBound = false; + } catch (NullPointerException e) { + Log.d(TAG, "Null messenger while attempting to send packet"); // NPE, routerServiceMessenger is null + stop(); onHardwareDisconnected(null, null); return false; } - } catch (NullPointerException e) { - Log.d(TAG, "Null messenger while attempting to send packet"); // NPE, routerServiceMessenger is null - routerServiceMessenger = null; - registeredWithRouterService = false; - unBindFromRouterService(); - isBound = false; - onHardwareDisconnected(null, null); + } else { + Log.e(TAG, "Unable to send message to router service. Not registered."); return false; } } else { - Log.e(TAG, "Unable to send message to router service. Not registered."); + Log.e(TAG, "Unable to send message to router service. Not bound."); return false; } - } else { - Log.e(TAG, "Unable to send message to router service. Not bound."); - return false; } } @@ -416,7 +411,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; @@ -448,7 +442,6 @@ public class TransportBroker { synchronized (INIT_LOCK) { unregisterWithRouterService(); routerServiceMessenger = null; - queuedOnTransportConnect = null; unBindFromRouterService(); isBound = false; } @@ -463,13 +456,12 @@ public class TransportBroker { unregisterWithRouterService(); unBindFromRouterService(); routerServiceMessenger = null; - queuedOnTransportConnect = null; currentContext = null; } } - private synchronized void unBindFromRouterService() { + private void unBindFromRouterService() { try { getContext().unbindService(routerConnection); @@ -487,27 +479,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 * @@ -518,7 +500,6 @@ public class TransportBroker { public boolean onHardwareConnected(TransportType type) { synchronized (INIT_LOCK) { if (routerServiceMessenger == null) { - queuedOnTransportConnect = type; return false; } return true; @@ -528,7 +509,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; @@ -616,7 +596,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 fe51e7711..234cbb22e 100644 --- a/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java @@ -121,7 +121,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) { Log.i(TAG, "Proxy is connected. Now initializing."); synchronized (this) { changeRegistrationRetry = 0; @@ -141,7 +141,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) { Log.i(TAG, "Proxy is closed."); if (reason == null || !reason.equals(SdlDisconnectedReason.LANGUAGE_CHANGE)) { dispose(); 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 034e5d951..2934e5736 100644 --- a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java @@ -143,7 +143,7 @@ abstract class BaseLifecycleManager { this.lifecycleListener = listener; this.minimumProtocolVersion = appConfig.getMinimumProtocolVersion(); this.minimumRPCVersion = appConfig.getMinimumRPCVersion(); - initializeProxy(); + initialize(); } public void start() { @@ -163,8 +163,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(); } @@ -174,7 +177,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(); } @@ -345,7 +348,7 @@ abstract class BaseLifecycleManager { void onClose(String info, Exception e, SdlDisconnectedReason reason) { Log.i(TAG, "onClose"); if (lifecycleListener != null) { - lifecycleListener.onProxyClosed((LifecycleManager) this, info, e, reason); + lifecycleListener.onClosed((LifecycleManager) this, info, e, reason); } } @@ -399,7 +402,7 @@ abstract class BaseLifecycleManager { UnregisterAppInterface msg = new UnregisterAppInterface(); msg.setCorrelationID(UNREGISTER_APP_INTERFACE_CORRELATION_ID); sendRPCMessagePrivate(msg, true); - cleanProxy(); + clean(); return; } processRaiResponse(raiResponse); @@ -410,7 +413,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: @@ -459,15 +462,15 @@ abstract class BaseLifecycleManager { if (!onAppInterfaceUnregistered.getReason().equals(AppInterfaceUnregisteredReason.LANGUAGE_CHANGE)) { Log.v(TAG, "on app interface unregistered"); - cleanProxy(); + clean(); } else { Log.v(TAG, "re-registering for language change"); - cycleProxy(SdlDisconnectedReason.LANGUAGE_CHANGE); + cycle(SdlDisconnectedReason.LANGUAGE_CHANGE); } break; case UNREGISTER_APP_INTERFACE: Log.v(TAG, "unregister app interface"); - cleanProxy(); + clean(); break; } } @@ -919,24 +922,24 @@ abstract class BaseLifecycleManager { @Override public void onProtocolSessionStartedNACKed(SessionType sessionType, byte sessionID, byte version, String correlationID, List<String> rejectedParams) { - Log.w(TAG, sessionType + " onProtocolSessionStartedNACKed " + sessionID + " RejectedParams: " + rejectedParams); - BaseLifecycleManager.this.onProtocolSessionStartedNACKed(sessionType); + Log.w(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) { Log.i(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 @@ -1173,174 +1176,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, 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; - } - } - /** * 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 @@ -1394,7 +1229,7 @@ abstract class BaseLifecycleManager { return null; } - void cleanProxy() { + void clean() { firstTimeFull = true; currentHMIStatus = null; if (rpcListeners != null) { @@ -1474,7 +1309,7 @@ abstract class BaseLifecycleManager { ********************************** Platform specific methods - START ************************************* *********************************************************************************************************/ - void initializeProxy() { + void initialize() { this.rpcListeners = new HashMap<>(); this.rpcResponseListeners = new HashMap<>(); this.rpcNotificationListeners = new HashMap<>(); @@ -1483,12 +1318,12 @@ abstract class BaseLifecycleManager { setupInternalRpcListeners(); } - void onProtocolSessionStarted(SessionType sessionType) { + void onServiceStarted(SessionType sessionType) { if (sessionType != null) { if (minimumProtocolVersion != null && minimumProtocolVersion.isNewerThan(getProtocolVersion()) == 1) { Log.w(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; } @@ -1525,32 +1360,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 onStartServiceNACKed(SessionType sessionType) { } - void onProtocolSessionEnded(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() { @@ -1559,4 +1387,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 dac4df830..8ab2caecd 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java @@ -51,6 +51,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; @@ -74,19 +75,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("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) { Log.e(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("SETTING UP screen manager, at least one sub manager is still setting up"); transitionToState(SETTING_UP); } else { @@ -115,6 +120,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(){ @@ -124,6 +130,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); } @@ -137,6 +144,7 @@ abstract class BaseScreenManager extends BaseSubManager { voiceCommandManager.dispose(); menuManager.dispose(); choiceSetManager.dispose(); + subscribeButtonManager.dispose(); super.dispose(); } @@ -578,4 +586,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 328527e8c..d53b64b9c 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 @@ -34,6 +34,7 @@ package com.smartdevicelink.java; 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; @@ -76,15 +77,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); @@ -192,6 +190,7 @@ public class SdlService { performWelcomeSpeak(); performWelcomeShow(); preloadChoices(); + subscribeToButtons(); } } }); @@ -328,6 +327,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 77ebafd2c..6a8ebe136 100644 --- a/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java +++ b/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java @@ -50,14 +50,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); + } +} |