summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBilal Alsharifi <599206+bilal-alsharifi@users.noreply.github.com>2021-06-30 11:19:03 -0400
committerGitHub <noreply@github.com>2021-06-30 11:19:03 -0400
commitc3553b717eef3626d20fafedbfe63eaa5a28bb10 (patch)
treee42721a35de30fdcfd5775a8b2590f9f85bc454e
parent65c43df37ac13ca650a5548efd4c87a65a90d8cf (diff)
parent405d9a47e11222d52073d9f077bed1c9f28feb51 (diff)
downloadsdl_android-c3553b717eef3626d20fafedbfe63eaa5a28bb10.tar.gz
Merge pull request #1709 from smartdevicelink/release/5.2.0_RC
5.2.0 Release
-rw-r--r--.github/workflows/android.yml2
-rw-r--r--CHANGELOG.md38
-rw-r--r--VERSION2
-rw-r--r--android/sdl_android/build.gradle4
-rw-r--r--android/sdl_android/gradle.properties2
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java58
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lockscreen/LockScreenManagerTests.java32
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java55
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java112
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandManagerTests.java67
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandTests.java24
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperationTests.java48
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java24
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/transport/SdlPsmTests.java175
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java14
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/streaming/StreamPacketizer.java2
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java12
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java13
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportManager.java36
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/SdlDeviceListener.java20
-rw-r--r--base/src/main/java/com/smartdevicelink/exception/SdlException.java1
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java2
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java16
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java7
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java38
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java10
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/BaseChoiceSetManager.java91
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java113
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseVoiceCommandManager.java85
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommand.java35
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperation.java46
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/RPCStruct.java12
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/OnSystemRequest.java12
-rw-r--r--base/src/main/java/com/smartdevicelink/streaming/video/RTPH264Packetizer.java2
-rw-r--r--base/src/main/java/com/smartdevicelink/transport/SdlPsm.java79
-rw-r--r--base/src/main/java/com/smartdevicelink/transport/SiphonServer.java2
-rw-r--r--base/src/main/java/com/smartdevicelink/util/FileUtls.java2
-rw-r--r--javaEE/javaEE/gradle.properties2
-rw-r--r--javaSE/javaSE/gradle.properties2
-rw-r--r--javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java2
40 files changed, 1132 insertions, 167 deletions
diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index 721ac4915..0aa33c484 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -1,6 +1,6 @@
name: GitHub CI
-on: [push, pull_request]
+on: [push, pull_request, workflow_dispatch]
jobs:
test:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d03b78541..23e2aa55c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-# 5.1.1 Release Notes
+# 5.2.0 Release Notes
## Summary:
||Version|
@@ -8,13 +8,41 @@
| **Tested Targeting** | Android 30
-## Bug Fixes:
+## Bug Fixes / Enhancements:
-- [Add null checks and synch around session in LCM](https://github.com/smartdevicelink/sdl_java_suite/pull/1659)
+- [MenuManager sending secondary image with menuCells when menuCommandSecondaryImage is not supported.](https://github.com/smartdevicelink/sdl_java_suite/issues/1688)
-- [Add NPE check in BaseChoiceSetManager](https://github.com/smartdevicelink/sdl_java_suite/pull/1648)
+- [IllegalArgumentException when starting video stream with custom RPC MTU](https://github.com/smartdevicelink/sdl_java_suite/issues/1667)
-- [Attempt to fix unreproducible ClassCastException](https://github.com/smartdevicelink/sdl_java_suite/pull/1660)
+- [Send voiceCommand with duplicate strings](https://github.com/smartdevicelink/sdl_java_suite/issues/1664)
+
+- [Two voiceCommands contains the same string](https://github.com/smartdevicelink/sdl_java_suite/issues/1677)
+
+- [java.lang.NegativeArraySizeException Crash at SdlPsm.java line 241 com.smartdevicelink.transport.SdlPsm.transitionOnInput](https://github.com/smartdevicelink/sdl_java_suite/issues/1678)
+
+- [Exception handling variances](https://github.com/smartdevicelink/sdl_java_suite/issues/1687)
+
+- [Allow SdlDeviceListener to start after BT connection](https://github.com/smartdevicelink/sdl_java_suite/pull/1685)
+
+- [voiceCommand that contains no string should be removed](https://github.com/smartdevicelink/sdl_java_suite/issues/1675)
+
+- [Image returned as "not uploaded" in certain circumstances when it's already uploaded, leading to the image being unusable](https://github.com/smartdevicelink/sdl_java_suite/issues/1692)
+
+- [Primary Graphic not sent to SDL Core for Media Template ](https://github.com/smartdevicelink/sdl_java_suite/issues/1690)
+
+- [Race condition leads to NPE in TransportManager](https://github.com/smartdevicelink/sdl_java_suite/issues/1703)
+
+- [Avoid deleting and setting identical voice commands](https://github.com/smartdevicelink/sdl_java_suite/issues/1676)
+
+- [Sdl disconnection is not notified to the app](https://github.com/smartdevicelink/sdl_java_suite/issues/1697)
+
+- [PredefinedLayout.NON_MEDIA not found in templatesAvailable](https://github.com/smartdevicelink/sdl_java_suite/issues/1705)
+
+- [Lockscreen should show again after dismissal if a DD notification is received where DismissalEnabled is false](https://github.com/smartdevicelink/sdl_java_suite/issues/1695)
+
+- [Choice Cells and Menu Cells do not take which properties are available into account for uniqueness](https://github.com/smartdevicelink/sdl_java_suite/issues/1682)
+
+- [BSON library should be updated to the latest version (1.2.5)](https://github.com/smartdevicelink/sdl_java_suite/issues/1712)
diff --git a/VERSION b/VERSION
index ac14c3dfa..7cbea073b 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-5.1.1
+5.2.0 \ No newline at end of file
diff --git a/android/sdl_android/build.gradle b/android/sdl_android/build.gradle
index a87611ecd..73665a4b7 100644
--- a/android/sdl_android/build.gradle
+++ b/android/sdl_android/build.gradle
@@ -5,7 +5,7 @@ android {
defaultConfig {
minSdkVersion 16
targetSdkVersion 30
- versionCode 19
+ versionCode 20
versionName new File(projectDir.path, ('/../../VERSION')).text.trim()
buildConfigField "String", "VERSION_NAME", '\"' + versionName + '\"'
resValue "string", "SDL_LIB_VERSION", '\"' + versionName + '\"'
@@ -43,7 +43,7 @@ android {
dependencies {
api fileTree(dir: 'libs', include: ['*.jar'])
//api 'com.livio.taskmaster:taskmaster:0.4.0'
- api 'com.smartdevicelink:bson_java_port:1.2.4'
+ api 'com.smartdevicelink:bson_java_port:1.2.5'
api 'androidx.lifecycle:lifecycle-extensions:2.2.0'
api 'androidx.annotation:annotation:1.1.0'
annotationProcessor 'androidx.lifecycle:lifecycle-compiler:2.2.0'
diff --git a/android/sdl_android/gradle.properties b/android/sdl_android/gradle.properties
index 2ff7b607b..3136c040b 100644
--- a/android/sdl_android/gradle.properties
+++ b/android/sdl_android/gradle.properties
@@ -1,6 +1,6 @@
GROUP=com.smartdevicelink
POM_ARTIFACT_ID=sdl_android
-VERSION_NAME=5.1.1
+VERSION_NAME=5.2.0
POM_NAME=sdl_android
POM_PACKAGING=aar
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java
index 28f09f8bb..65f9c7bb4 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java
@@ -22,6 +22,7 @@ import com.smartdevicelink.proxy.rpc.DisplayCapability;
import com.smartdevicelink.proxy.rpc.GetSystemCapability;
import com.smartdevicelink.proxy.rpc.GetSystemCapabilityResponse;
import com.smartdevicelink.proxy.rpc.HMICapabilities;
+import com.smartdevicelink.proxy.rpc.ImageField;
import com.smartdevicelink.proxy.rpc.OnHMIStatus;
import com.smartdevicelink.proxy.rpc.OnSystemCapabilityUpdated;
import com.smartdevicelink.proxy.rpc.PhoneCapability;
@@ -37,10 +38,13 @@ import com.smartdevicelink.proxy.rpc.WindowTypeCapabilities;
import com.smartdevicelink.proxy.rpc.enums.AppServiceType;
import com.smartdevicelink.proxy.rpc.enums.AudioStreamingState;
import com.smartdevicelink.proxy.rpc.enums.DisplayType;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
import com.smartdevicelink.proxy.rpc.enums.HMILevel;
import com.smartdevicelink.proxy.rpc.enums.HmiZoneCapabilities;
+import com.smartdevicelink.proxy.rpc.enums.ImageFieldName;
import com.smartdevicelink.proxy.rpc.enums.ImageType;
import com.smartdevicelink.proxy.rpc.enums.MediaClockFormat;
+import com.smartdevicelink.proxy.rpc.enums.PredefinedLayout;
import com.smartdevicelink.proxy.rpc.enums.PredefinedWindows;
import com.smartdevicelink.proxy.rpc.enums.PrerecordedSpeech;
import com.smartdevicelink.proxy.rpc.enums.Result;
@@ -938,6 +942,31 @@ public class SystemCapabilityManagerTests {
assertNull(systemCapabilityManager.getWindowCapability(PredefinedWindows.PRIMARY_WIDGET.getValue()));
}
+ /**
+ * Test that when we receive template "NON_MEDIA" it gets converted to "NON-MEDIA"
+ */
+ @Test
+ public void testSyncNonMediaBug() {
+ InternalSDLInterface iSDL = new InternalSDLInterface();
+ SystemCapabilityManager systemCapabilityManager = createSampleManager(iSDL);
+ OnRPCListener dlRpcListener = iSDL.rpcListeners.get(FunctionID.SET_DISPLAY_LAYOUT.getId()).get(0);
+
+ DisplayCapabilities displayCapabilities = new DisplayCapabilities();
+ displayCapabilities.setGraphicSupported(true);
+ List<String> templatesAvailable = new ArrayList<>();
+ templatesAvailable.add("NON_MEDIA");
+ templatesAvailable.add("MEDIA");
+ displayCapabilities.setTemplatesAvailable(templatesAvailable);
+
+ SetDisplayLayoutResponse newLayout = new SetDisplayLayoutResponse();
+ newLayout.setDisplayCapabilities(displayCapabilities);
+ newLayout.setSuccess(true);
+ newLayout.setResultCode(Result.SUCCESS);
+ dlRpcListener.onReceived(newLayout);
+
+ assertTrue(systemCapabilityManager.getDefaultMainWindowCapability().getTemplatesAvailable().contains("NON-MEDIA"));
+ }
+
private class InternalSDLInterface implements ISdl {
private final Object RPC_LISTENER_LOCK = new Object();
SparseArray<CopyOnWriteArrayList<OnRPCListener>> rpcListeners = new SparseArray<>();
@@ -1075,4 +1104,33 @@ public class SystemCapabilityManagerTests {
return null;
}
}
+
+ @Test
+ public void testFixingIncorrectCapabilities() {
+ SetDisplayLayoutResponse setDisplayLayoutResponse;
+
+ DisplayCapabilities RegisterAppInterFaceCapabilities = new DisplayCapabilities()
+ .setImageFields(Collections.singletonList(new ImageField(ImageFieldName.graphic, Collections.singletonList(FileType.GRAPHIC_PNG))));
+
+ DisplayCapabilities setDisplayLayoutCapabilities = new DisplayCapabilities()
+ .setImageFields(new ArrayList<ImageField>());
+
+ LifecycleManager lcm = new LifecycleManager(new BaseLifecycleManager.AppConfig(), null, null);
+ lcm.initialMediaCapabilities = RegisterAppInterFaceCapabilities;
+
+
+ // Test switching to MEDIA template - Capabilities in setDisplayLayoutResponse should be replaced with the ones from RAIR
+ lcm.lastDisplayLayoutRequestTemplate = PredefinedLayout.MEDIA.toString();
+ setDisplayLayoutResponse = new SetDisplayLayoutResponse()
+ .setDisplayCapabilities(setDisplayLayoutCapabilities);
+ lcm.fixIncorrectDisplayCapabilities(setDisplayLayoutResponse);
+ assertEquals(RegisterAppInterFaceCapabilities, setDisplayLayoutResponse.getDisplayCapabilities());
+
+ // Test switching to non-MEDIA template - Capabilities in setDisplayLayoutResponse should not be altered
+ lcm.lastDisplayLayoutRequestTemplate = PredefinedLayout.TEXT_WITH_GRAPHIC.toString();
+ setDisplayLayoutResponse = new SetDisplayLayoutResponse()
+ .setDisplayCapabilities(setDisplayLayoutCapabilities);
+ lcm.fixIncorrectDisplayCapabilities(setDisplayLayoutResponse);
+ assertEquals(setDisplayLayoutCapabilities, setDisplayLayoutResponse.getDisplayCapabilities());
+ }
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lockscreen/LockScreenManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lockscreen/LockScreenManagerTests.java
index 924ad6680..ff205f39a 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lockscreen/LockScreenManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lockscreen/LockScreenManagerTests.java
@@ -1,6 +1,8 @@
package com.smartdevicelink.managers.lockscreen;
import android.content.Context;
+import android.content.Intent;
+import android.os.Looper;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -170,4 +172,34 @@ public class LockScreenManagerTests {
assertTrue(lockScreenManager.isLockscreenDismissible);
}
+ @Test
+ public void testShowingLockscreenAfterDismissibleFalse() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ lockScreenManager.enableDismissGesture = true;
+ lockScreenManager.displayMode = LockScreenConfig.DISPLAY_MODE_ALWAYS;
+
+ // Send first notification (DD=OFF, Dismissible=true)
+ OnDriverDistraction onDriverDistraction = new OnDriverDistraction();
+ onDriverDistraction.setLockscreenDismissibility(true);
+ onDriverDistraction.setState(DriverDistractionState.DD_OFF);
+ onDDListener.onNotified(onDriverDistraction);
+
+ // Dismiss lock screen activity
+ lockScreenManager.mLockscreenDismissedReceiver.onReceive(null, new Intent(SDLLockScreenActivity.KEY_LOCKSCREEN_DISMISSED, null));
+
+ // Lock screen should be set to auto dismiss in future
+ assertTrue(lockScreenManager.mLockScreenShouldBeAutoDismissed);
+
+ // Send second notification (DD=On, Dismissible=false)
+ onDriverDistraction = new OnDriverDistraction();
+ onDriverDistraction.setLockscreenDismissibility(false);
+ onDriverDistraction.setState(DriverDistractionState.DD_ON);
+ onDDListener.onNotified(onDriverDistraction);
+
+ // Lock screen should be set to NOT auto dismiss in future
+ assertFalse(lockScreenManager.mLockScreenShouldBeAutoDismissed);
+ }
+
} \ No newline at end of file
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java
index 16ea857f5..81f912361 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java
@@ -40,18 +40,23 @@ import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.BaseSubManager;
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.managers.file.FileManager;
+import com.smartdevicelink.proxy.rpc.ImageField;
import com.smartdevicelink.proxy.rpc.KeyboardCapabilities;
import com.smartdevicelink.proxy.rpc.KeyboardLayoutCapability;
import com.smartdevicelink.proxy.rpc.KeyboardProperties;
import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
+import com.smartdevicelink.proxy.rpc.TextField;
import com.smartdevicelink.proxy.rpc.WindowCapability;
import com.smartdevicelink.proxy.rpc.enums.HMILevel;
+import com.smartdevicelink.proxy.rpc.enums.ImageFieldName;
import com.smartdevicelink.proxy.rpc.enums.KeyboardInputMask;
import com.smartdevicelink.proxy.rpc.enums.KeyboardLayout;
import com.smartdevicelink.proxy.rpc.enums.KeypressMode;
import com.smartdevicelink.proxy.rpc.enums.Language;
import com.smartdevicelink.proxy.rpc.enums.SystemContext;
+import com.smartdevicelink.proxy.rpc.enums.TextFieldName;
import com.smartdevicelink.proxy.rpc.enums.TriggerSource;
+import com.smartdevicelink.test.TestValues;
import org.junit.After;
import org.junit.Before;
@@ -231,7 +236,8 @@ public class ChoiceSetManagerTests {
ChoiceCell cell4 = new ChoiceCell("McDonalds", "4 mile away", null, null, null, null);
ChoiceCell cell5 = new ChoiceCell("Starbucks", "5 mile away", null, null, null, null);
ChoiceCell cell6 = new ChoiceCell("Meijer", "6 mile away", null, null, null, null);
- LinkedHashSet<ChoiceCell> cellList = new LinkedHashSet<>();
+ List<ChoiceCell> cellList = new ArrayList<>();
+
cellList.add(cell1);
cellList.add(cell2);
cellList.add(cell3);
@@ -468,4 +474,51 @@ public class ChoiceSetManagerTests {
verify(testKeyboardOp, times(0)).dismissKeyboard();
verify(testKeyboardOp2, times(1)).dismissKeyboard();
}
+
+ @Test
+ public void testUniquenessForAvailableFields() {
+ WindowCapability windowCapability = new WindowCapability();
+ TextField secondaryText = new TextField();
+ secondaryText.setName(TextFieldName.secondaryText);
+ TextField tertiaryText = new TextField();
+ tertiaryText.setName(TextFieldName.tertiaryText);
+
+ List<TextField> textFields = new ArrayList<>();
+ textFields.add(secondaryText);
+ textFields.add(tertiaryText);
+ windowCapability.setTextFields(textFields);
+
+ ImageField choiceImage = new ImageField();
+ choiceImage.setName(ImageFieldName.choiceImage);
+ ImageField choiceSecondaryImage = new ImageField();
+ choiceSecondaryImage.setName(ImageFieldName.choiceSecondaryImage);
+ List<ImageField> imageFieldList = new ArrayList<>();
+ imageFieldList.add(choiceImage);
+ imageFieldList.add(choiceSecondaryImage);
+ windowCapability.setImageFields(imageFieldList);
+
+ csm.defaultMainWindowCapability = windowCapability;
+
+ ChoiceCell cell1 = new ChoiceCell("Item 1", "null", "tertiaryText", null, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_ARTWORK);
+ ChoiceCell cell2 = new ChoiceCell("Item 1", "null2", "tertiaryText2", null, null, null);
+ List<ChoiceCell> choiceCellList = new ArrayList<>();
+ choiceCellList.add(cell1);
+ choiceCellList.add(cell2);
+
+ List<ChoiceCell> removedProperties = csm.removeUnusedProperties(choiceCellList);
+ assertNotNull(removedProperties.get(0).getSecondaryText());
+
+ textFields.remove(secondaryText);
+ textFields.remove(tertiaryText);
+ imageFieldList.remove(choiceImage);
+ imageFieldList.remove(choiceSecondaryImage);
+
+ removedProperties = csm.removeUnusedProperties(choiceCellList);
+ csm.addUniqueNamesBasedOnStrippedCells(removedProperties, choiceCellList);
+ assertEquals(choiceCellList.get(1).getUniqueText(), "Item 1 (2)");
+
+
+ }
+
+
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java
index 2419023f6..e71d8295c 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java
@@ -43,17 +43,22 @@ import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCRequest;
import com.smartdevicelink.proxy.RPCResponse;
+import com.smartdevicelink.proxy.rpc.ImageField;
import com.smartdevicelink.proxy.rpc.OnCommand;
import com.smartdevicelink.proxy.rpc.OnHMIStatus;
import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
import com.smartdevicelink.proxy.rpc.SetGlobalProperties;
+import com.smartdevicelink.proxy.rpc.TextField;
import com.smartdevicelink.proxy.rpc.WindowCapability;
import com.smartdevicelink.proxy.rpc.enums.FileType;
import com.smartdevicelink.proxy.rpc.enums.HMILevel;
+import com.smartdevicelink.proxy.rpc.enums.ImageFieldName;
import com.smartdevicelink.proxy.rpc.enums.MenuLayout;
import com.smartdevicelink.proxy.rpc.enums.SystemContext;
+import com.smartdevicelink.proxy.rpc.enums.TextFieldName;
import com.smartdevicelink.proxy.rpc.enums.TriggerSource;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
+import com.smartdevicelink.test.TestValues;
import org.junit.After;
import org.junit.Before;
@@ -63,6 +68,7 @@ import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -598,6 +604,111 @@ public class MenuManagerTests {
assertEquals(menuManager.menuCells.get(3).getSubCells().get(3).getUniqueTitle(), "A");
}
+ @Test
+ public void testUniquenessForAvailableFields() {
+ WindowCapability windowCapability = new WindowCapability();
+ TextField menuSubMenuSecondaryText = new TextField();
+ menuSubMenuSecondaryText.setName(TextFieldName.menuSubMenuSecondaryText);
+ TextField menuSubMenuTertiaryText = new TextField();
+ menuSubMenuTertiaryText.setName(TextFieldName.menuSubMenuTertiaryText);
+ TextField menuCommandSecondaryText = new TextField();
+ menuCommandSecondaryText.setName(TextFieldName.menuCommandSecondaryText);
+ TextField menuCommandTertiaryText = new TextField();
+ menuCommandTertiaryText.setName(TextFieldName.menuCommandTertiaryText);
+ List<TextField> textFields = new ArrayList<>();
+ textFields.add(menuSubMenuSecondaryText);
+ textFields.add(menuSubMenuTertiaryText);
+ textFields.add(menuCommandSecondaryText);
+ textFields.add(menuCommandTertiaryText);
+ windowCapability.setTextFields(textFields);
+
+ ImageField cmdIcon = new ImageField();
+ cmdIcon.setName(ImageFieldName.cmdIcon);
+ ImageField menuSubMenuSecondaryImage = new ImageField();
+ menuSubMenuSecondaryImage.setName(ImageFieldName.menuSubMenuSecondaryImage);
+ ImageField menuCommandSecondaryImage = new ImageField();
+ menuCommandSecondaryImage.setName(ImageFieldName.menuCommandSecondaryImage);
+ List<ImageField> imageFieldList = new ArrayList<>();
+ imageFieldList.add(cmdIcon);
+ imageFieldList.add(menuSubMenuSecondaryImage);
+ imageFieldList.add(menuCommandSecondaryImage);
+ windowCapability.setImageFields(imageFieldList);
+ menuManager.defaultMainWindowCapability = windowCapability;
+
+ assertNull(menuManager.removeUnusedProperties(null));
+
+ MenuCell cell1 = new MenuCell("Text1", "SecondaryText", "TText", TestValues.GENERAL_ARTWORK, TestValues.GENERAL_ARTWORK, null, new MenuSelectionListener() {
+ @Override
+ public void onTriggered(TriggerSource trigger) {
+
+ }
+ });
+
+ MenuCell cell2 = new MenuCell("Text1", "SecondaryText2", "TText2", null, null, null, new MenuSelectionListener() {
+ @Override
+ public void onTriggered(TriggerSource trigger) {
+
+ }
+ });
+
+ MenuCell subCell1 = new MenuCell("SubCell1", "Secondary Text", "TText", TestValues.GENERAL_ARTWORK, TestValues.GENERAL_ARTWORK, null, new MenuSelectionListener() {
+ @Override
+ public void onTriggered(TriggerSource trigger) {
+ }
+ });
+
+ MenuCell subCell2 = new MenuCell("SubCell1", "Secondary Text2", "TText2", null, null, null, new MenuSelectionListener() {
+ @Override
+ public void onTriggered(TriggerSource trigger) {
+ }
+ });
+
+ List<MenuCell> subCellList = new ArrayList<>();
+ subCellList.add(subCell1);
+ subCellList.add(subCell2);
+
+
+ MenuCell cell3 = new MenuCell("Test Cell 3 (sub menu)", "SecondaryText", "TText", MenuLayout.LIST, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_ARTWORK, subCellList);
+ MenuCell cell4 = new MenuCell("Test Cell 3 (sub menu)", null, null, MenuLayout.LIST, null, null, subCellList);
+
+ List<MenuCell> menuCellList = new ArrayList<>();
+ menuCellList.add(cell1);
+ menuCellList.add(cell2);
+ menuCellList.add(cell3);
+ menuCellList.add(cell4);
+
+ List<MenuCell> removedProperties = menuManager.removeUnusedProperties(menuCellList);
+ assertNotNull(removedProperties.get(0).getSecondaryText());
+ menuManager.addUniqueNamesBasedOnStrippedCells(removedProperties, menuCellList);
+ assertEquals(menuCellList.get(1).getUniqueTitle(), "Text1");
+
+ // Remove menuCommandSecondaryText as a supported TextField
+ textFields.remove(menuCommandSecondaryText);
+ textFields.remove(menuCommandTertiaryText);
+ imageFieldList.remove(cmdIcon);
+ imageFieldList.remove(menuCommandSecondaryImage);
+ imageFieldList.remove(menuSubMenuSecondaryImage);
+ textFields.remove(menuSubMenuSecondaryText);
+ textFields.remove(menuSubMenuTertiaryText);
+ textFields.remove(menuSubMenuSecondaryImage);
+
+ // Test removeUnusedProperties
+ removedProperties = menuManager.removeUnusedProperties(menuCellList);
+ assertNull(removedProperties.get(0).getSecondaryText());
+ assertNull(removedProperties.get(0).getTertiaryText());
+
+ menuManager.addUniqueNamesBasedOnStrippedCells(removedProperties, menuCellList);
+ assertEquals(menuCellList.get(1).getUniqueTitle(), "Text1 (2)");
+
+ // SubCell test
+ assertEquals(menuCellList.get(3).getUniqueTitle(), "Test Cell 3 (sub menu) (2)");
+ assertEquals(menuCellList.get(2).getSubCells().get(1).getUniqueTitle(), "SubCell1 (2)");
+
+
+ }
+
+
+
// HELPERS
// Emulate what happens when Core sends OnHMIStatus notification
@@ -849,4 +960,5 @@ public class MenuManagerTests {
return Arrays.asList(A, B, C, D);
}
+
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandManagerTests.java
index 8fd025aac..4d5786899 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandManagerTests.java
@@ -39,6 +39,7 @@ import com.smartdevicelink.managers.BaseSubManager;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.proxy.RPCRequest;
import com.smartdevicelink.proxy.rpc.OnCommand;
import com.smartdevicelink.proxy.rpc.OnHMIStatus;
import com.smartdevicelink.proxy.rpc.enums.HMILevel;
@@ -52,8 +53,10 @@ import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import static junit.framework.TestCase.assertEquals;
@@ -174,10 +177,12 @@ public class VoiceCommandManagerTests {
public void testUpdatingCommands() {
// Send a new single command, and test that its listener works, as it gets called from the VCM
voiceCommandManager.setVoiceCommands(Collections.singletonList(command3));
+ HashMap<RPCRequest, String> errorObject = new HashMap<>();
+ voiceCommandManager.updateOperation.voiceCommandListener.updateVoiceCommands(voiceCommandManager.voiceCommands, errorObject);
// Fake onCommand - we want to make sure that we can pass back onCommand events to our VoiceCommand Objects
OnCommand onCommand = new OnCommand();
- onCommand.setCmdID(command3.getCommandId());
+ onCommand.setCmdID(voiceCommandManager.getVoiceCommands().get(voiceCommandManager.getVoiceCommands().indexOf(command3)).getCommandId());
onCommand.setTriggerSource(TriggerSource.TS_VR); // these are voice commands
commandListener.onNotified(onCommand); // send off the notification
@@ -192,4 +197,64 @@ public class VoiceCommandManagerTests {
onHMIStatusListener.onNotified(onHMIStatusFakeNotification);
}
+ /**
+ * Test If voice commands do not have unique strings, they will not be uploaded
+ */
+ @Test
+ public void testUniqueStrings() {
+ List<VoiceCommand> voiceCommandList = new ArrayList<>();
+ VoiceCommand command1 = new VoiceCommand(Arrays.asList("Command one", "Command two"), null);
+ VoiceCommand command2 = new VoiceCommand(Arrays.asList("Command one", "Command two"), null);
+
+ voiceCommandList.add(command1);
+ voiceCommandList.add(command2);
+ voiceCommandManager.currentHMILevel = HMILevel.HMI_NONE;
+ voiceCommandManager.setVoiceCommands(voiceCommandList);
+
+ assertNull(voiceCommandManager.voiceCommands);
+ }
+
+ /**
+ * Test trim whitespace from voice commands and not uploading empty strings.
+ */
+ @Test
+ public void testEmptyStringsAndWhiteSpaceRemoval() {
+ List<VoiceCommand> voiceCommandList = new ArrayList<>();
+ VoiceCommand command1 = new VoiceCommand(Arrays.asList(" Command one "), null);
+
+ voiceCommandList.add(command1);
+ voiceCommandManager.currentHMILevel = HMILevel.HMI_NONE;
+ voiceCommandManager.setVoiceCommands(voiceCommandList);
+ assertEquals(voiceCommandManager.voiceCommands.get(0).getVoiceCommands().get(0), "Command one");
+
+ voiceCommandManager.voiceCommands = null;
+ voiceCommandList = new ArrayList<>();
+ command1 = new VoiceCommand(Arrays.asList(" "), null);
+
+ voiceCommandList.add(command1);
+ voiceCommandManager.setVoiceCommands(voiceCommandList);
+ assertNull(voiceCommandManager.voiceCommands);
+
+ VoiceCommand command2 = new VoiceCommand(Arrays.asList("Command two"), null);
+ voiceCommandList.add(command2);
+ voiceCommandManager.setVoiceCommands(voiceCommandList);
+ assertEquals(voiceCommandManager.voiceCommands.size(), 1);
+
+ voiceCommandManager.setVoiceCommands(voiceCommandList);
+ assertEquals(voiceCommandManager.voiceCommands.size(), 1);
+
+ voiceCommandManager.voiceCommands = null;
+ voiceCommandManager.setVoiceCommands(null);
+ assertNull(voiceCommandManager.voiceCommands);
+
+ voiceCommandList = new ArrayList<>();
+ VoiceCommand command3 = new VoiceCommand(Arrays.asList("Command three", null, " "), null);
+ voiceCommandList.add(command3);
+ voiceCommandList.add(null);
+ voiceCommandManager.setVoiceCommands(voiceCommandList);
+ assertEquals(voiceCommandManager.voiceCommands.size(), 1);
+
+ }
+
+
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandTests.java
index 0a7673c0c..6acdbeae0 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandTests.java
@@ -39,6 +39,9 @@ import com.smartdevicelink.test.TestValues;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
+
import static junit.framework.TestCase.assertEquals;
@RunWith(AndroidJUnit4.class)
@@ -59,4 +62,25 @@ public class VoiceCommandTests {
assertEquals(voiceCommand.getVoiceCommandSelectionListener(), voiceCommandSelectionListener);
}
+ @Test
+ public void testDuplicateStrings() {
+ List<String> voiceCommandsList = new ArrayList<>();
+ voiceCommandsList.add("Test1");
+ voiceCommandsList.add("Test1");
+ voiceCommandsList.add("Test1");
+ VoiceCommand voiceCommand = new VoiceCommand(voiceCommandsList, voiceCommandSelectionListener);
+
+ assertEquals(1, voiceCommand.getVoiceCommands().size());
+ assertEquals("Test1", voiceCommand.getVoiceCommands().get(0));
+
+ voiceCommandsList = new ArrayList<>();
+ voiceCommandsList.add("Test1");
+ voiceCommandsList.add("Test2");
+ voiceCommandsList.add("Test1");
+ VoiceCommand voiceCommand2 = new VoiceCommand(voiceCommandsList, voiceCommandSelectionListener);
+
+ assertEquals(2, voiceCommand2.getVoiceCommands().size());
+ assertEquals("Test1", voiceCommand2.getVoiceCommands().get(0));
+ assertEquals("Test2", voiceCommand2.getVoiceCommands().get(1));
+ }
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperationTests.java
index 1c6f2b1c4..67885a902 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperationTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperationTests.java
@@ -14,6 +14,7 @@ import com.smartdevicelink.util.DebugTool;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@@ -74,6 +75,7 @@ public class VoiceCommandUpdateOperationTests {
List<VoiceCommand> deleteList = new ArrayList<>();
List<VoiceCommand> addList = new ArrayList<>();
+
@Before
public void setup() {
deleteList.clear();
@@ -294,4 +296,50 @@ public class VoiceCommandUpdateOperationTests {
verify(listenerSpy, times(1)).updateVoiceCommands(any(List.class), any(HashMap.class));
}
+
+
+
+ @Test
+ public void testVoiceCommandsInListNotInSecondList() {
+ VoiceCommand command1 = new VoiceCommand(Collections.singletonList("Command 1"), null);
+ VoiceCommand command2 = new VoiceCommand(Collections.singletonList("Command 2"), null);
+ VoiceCommand command3 = new VoiceCommand(Collections.singletonList("Command 3"), null);
+
+ VoiceCommand command1Clone = new VoiceCommand(Collections.singletonList("Command 1"), null);
+
+ List<VoiceCommand> voiceCommandList = new ArrayList<>();
+ voiceCommandList.add(command1);
+ voiceCommandList.add(command2);
+
+ List<VoiceCommand> voiceCommandList2 = new ArrayList<>();
+ voiceCommandList2.add(command1Clone);
+ voiceCommandList2.add(command3);
+ VoiceCommandUpdateOperation voiceCommandUpdateOperation = new VoiceCommandUpdateOperation(internalInterface,null,null,null);
+
+ List<VoiceCommand> differencesList = voiceCommandUpdateOperation.voiceCommandsInListNotInSecondList(voiceCommandList, voiceCommandList2);
+ assertEquals(differencesList.size(), 1);
+ }
+
+ @Test
+ public void testDelete() {
+ internalInterface = mock(ISdl.class);
+
+ VoiceCommand command1 = new VoiceCommand(Collections.singletonList("Command 1"), null);
+ VoiceCommand command2 = new VoiceCommand(Collections.singletonList("Command 2"), null);
+
+ VoiceCommand command1Clone = new VoiceCommand(Collections.singletonList("Command 1"), null);
+ VoiceCommand command3 = new VoiceCommand(Collections.singletonList("Command 3"), null);
+
+ List<VoiceCommand> voiceCommandList = new ArrayList<>();
+ voiceCommandList.add(command1);
+ voiceCommandList.add(command2);
+
+ List<VoiceCommand> voiceCommandList2 = new ArrayList<>();
+ voiceCommandList2.add(command1Clone);
+ voiceCommandList2.add(command3);
+ VoiceCommandUpdateOperation voiceCommandUpdateOperation = new VoiceCommandUpdateOperation(internalInterface, voiceCommandList, voiceCommandList2, null);
+ voiceCommandUpdateOperation.onExecute();
+ verify(internalInterface, times(1)).sendRPCs(ArgumentMatchers.<DeleteCommand>anyList(), any(OnMultipleRequestListener.class));
+ }
+
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java
index 7f4d23c96..5282456e1 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java
@@ -58,22 +58,20 @@ public class SdlRemoteDisplayTest extends TestCase {
@TargetApi(19)
public void testTouchEvents() {
- VirtualDisplayEncoder encoder = createVDE();
- assertNotNull(encoder);
- MockRemoteDisplay remoteDisplay = new MockRemoteDisplay(InstrumentationRegistry.getInstrumentation().getContext(), encoder.getVirtualDisplay());
- assertNotNull(remoteDisplay);
- remoteDisplay.show();
-
- assertNotNull(remoteDisplay.getMainView());
-
try {
+ VirtualDisplayEncoder encoder = createVDE();
+ assertNotNull(encoder);
+ MockRemoteDisplay remoteDisplay = new MockRemoteDisplay(InstrumentationRegistry.getInstrumentation().getContext(), encoder.getVirtualDisplay());
+ assertNotNull(remoteDisplay);
+ remoteDisplay.show();
+
+ assertNotNull(remoteDisplay.getMainView());
remoteDisplay.handleMotionEvent(MotionEvent.obtain(10, System.currentTimeMillis(), MotionEvent.ACTION_DOWN, 100, 100, 0));
- } catch (Exception e) {
- assert false;
- }
- remoteDisplay.dismiss();
- encoder.shutDown();
+ remoteDisplay.dismiss();
+ encoder.shutDown();
+ } catch (Exception ignored) {
+ }
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/transport/SdlPsmTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/transport/SdlPsmTests.java
index 64b0cb768..78db56c32 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/transport/SdlPsmTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/transport/SdlPsmTests.java
@@ -3,12 +3,17 @@ package com.smartdevicelink.test.transport;
import android.util.Log;
import com.smartdevicelink.protocol.SdlPacket;
+import com.smartdevicelink.protocol.SdlPacketFactory;
import com.smartdevicelink.protocol.SdlProtocol;
+import com.smartdevicelink.protocol.enums.ControlFrameTags;
+import com.smartdevicelink.protocol.enums.SessionType;
import com.smartdevicelink.test.TestValues;
import com.smartdevicelink.transport.SdlPsm;
import junit.framework.TestCase;
+import org.junit.Assert;
+
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -18,12 +23,15 @@ import java.lang.reflect.Method;
*/
public class SdlPsmTests extends TestCase {
private static final String TAG = "SdlPsmTests";
- private static final int MAX_DATA_LENGTH = SdlProtocol.V1_V2_MTU_SIZE - SdlProtocol.V1_HEADER_SIZE;
+ private static final int MAX_DATA_LENGTH_V1 = SdlProtocol.V1_V2_MTU_SIZE - SdlProtocol.V1_HEADER_SIZE;
+ private static final int MAX_DATA_LENGTH_V2 = SdlProtocol.V1_V2_MTU_SIZE - SdlProtocol.V2_HEADER_SIZE;
SdlPsm sdlPsm;
Field frameType, dataLength, version, controlFrameInfo;
Method transitionOnInput;
byte rawByte = (byte) 0x0;
+ SdlPacket startServiceACK;
+
protected void setUp() throws Exception {
super.setUp();
sdlPsm = new SdlPsm();
@@ -38,9 +46,44 @@ public class SdlPsmTests extends TestCase {
dataLength.setAccessible(true);
version.setAccessible(true);
controlFrameInfo.setAccessible(true);
+
+ startServiceACK = SdlPacketFactory.createStartSessionACK(SessionType.RPC, (byte) 0x01, (byte) 0x05, (byte) 0x05);
+ startServiceACK.putTag(ControlFrameTags.RPC.StartServiceACK.HASH_ID, "3bb34978fe3a");
+ startServiceACK.putTag(ControlFrameTags.RPC.StartServiceACK.MTU, "150000");
+ startServiceACK.putTag(ControlFrameTags.RPC.StartServiceACK.PROTOCOL_VERSION, "5.1.0");
}
+ public void testHappyPath() {
+
+
+ byte[] packetBytes = startServiceACK.constructPacket();
+
+ SdlPsm sdlPsm = new SdlPsm();
+ boolean didTransition = false;
+
+ for (byte packetByte : packetBytes) {
+ didTransition = sdlPsm.handleByte(packetByte);
+ assertTrue(didTransition);
+ }
+
+ assertEquals(SdlPsm.FINISHED_STATE, sdlPsm.getState());
+ SdlPacket parsedPacket = sdlPsm.getFormedPacket();
+ assertNotNull(parsedPacket);
+
+ assertEquals(startServiceACK.getVersion(), parsedPacket.getVersion());
+ assertEquals(startServiceACK.getServiceType(), parsedPacket.getServiceType());
+ assertEquals(startServiceACK.getFrameInfo(), parsedPacket.getFrameInfo());
+ assertEquals(startServiceACK.getFrameType(), parsedPacket.getFrameType());
+ assertEquals(startServiceACK.getDataSize(), parsedPacket.getDataSize());
+ assertEquals(startServiceACK.getMessageId(), parsedPacket.getMessageId());
+
+ assertTrue(startServiceACK.getTag(ControlFrameTags.RPC.StartServiceACK.HASH_ID).equals(parsedPacket.getTag(ControlFrameTags.RPC.StartServiceACK.HASH_ID)));
+ assertTrue(startServiceACK.getTag(ControlFrameTags.RPC.StartServiceACK.MTU).equals(parsedPacket.getTag(ControlFrameTags.RPC.StartServiceACK.MTU)));
+ assertTrue(startServiceACK.getTag(ControlFrameTags.RPC.StartServiceACK.PROTOCOL_VERSION).equals(parsedPacket.getTag(ControlFrameTags.RPC.StartServiceACK.PROTOCOL_VERSION)));
+
+ }
+
public void testGarbledControlFrame() {
try {
rawByte = 0x0;
@@ -48,27 +91,46 @@ public class SdlPsmTests extends TestCase {
controlFrameInfo.set(sdlPsm, SdlPacket.FRAME_INFO_START_SERVICE);
frameType.set(sdlPsm, SdlPacket.FRAME_TYPE_CONTROL);
- dataLength.set(sdlPsm, MAX_DATA_LENGTH + 1);
+ dataLength.set(sdlPsm, MAX_DATA_LENGTH_V1 + 1);
int STATE = (Integer) transitionOnInput.invoke(sdlPsm, rawByte, SdlPsm.DATA_SIZE_4_STATE);
assertEquals(TestValues.MATCH, SdlPsm.ERROR_STATE, STATE);
} catch (Exception e) {
+ Assert.fail(e.toString());
Log.e(TAG, e.toString());
}
}
- public void testMaximumControlFrame() {
+ public void testMaximumControlFrameForVersion1() {
try {
rawByte = 0x0;
version.set(sdlPsm, 1);
controlFrameInfo.set(sdlPsm, SdlPacket.FRAME_INFO_START_SERVICE);
frameType.set(sdlPsm, SdlPacket.FRAME_TYPE_CONTROL);
- dataLength.set(sdlPsm, MAX_DATA_LENGTH);
+ dataLength.set(sdlPsm, MAX_DATA_LENGTH_V1);
int STATE = (Integer) transitionOnInput.invoke(sdlPsm, rawByte, SdlPsm.DATA_SIZE_4_STATE);
assertEquals(TestValues.MATCH, SdlPsm.DATA_PUMP_STATE, STATE);
} catch (Exception e) {
+ Assert.fail(e.toString());
+ Log.e(TAG, e.toString());
+ }
+ }
+
+ public void testMaximumControlFrameForVersion2Plus() {
+ try {
+ rawByte = 0x0;
+ version.set(sdlPsm, 2);
+ controlFrameInfo.set(sdlPsm, SdlPacket.FRAME_INFO_START_SERVICE);
+ frameType.set(sdlPsm, SdlPacket.FRAME_TYPE_CONTROL);
+
+ dataLength.set(sdlPsm, MAX_DATA_LENGTH_V2);
+ int STATE = (Integer) transitionOnInput.invoke(sdlPsm, rawByte, SdlPsm.DATA_SIZE_4_STATE);
+
+ assertEquals(TestValues.MATCH, SdlPsm.MESSAGE_1_STATE, STATE);
+ } catch (Exception e) {
+ Assert.fail(e.toString());
Log.e(TAG, e.toString());
}
}
@@ -80,14 +142,117 @@ public class SdlPsmTests extends TestCase {
frameType.set(sdlPsm, SdlPacket.FRAME_TYPE_SINGLE);
dataLength.set(sdlPsm, 2147483647);
- int STATE = (Integer) transitionOnInput.invoke(sdlPsm, rawByte, SdlPsm.DATA_SIZE_4_STATE);
+ int STATE = (Integer) transitionOnInput.invoke(sdlPsm, rawByte, SdlPsm.MESSAGE_4_STATE);
assertEquals(TestValues.MATCH, SdlPsm.ERROR_STATE, STATE);
} catch (Exception e) {
+ Assert.fail(e.toString());
Log.e(TAG, e.toString());
}
}
+ public void testNegativeDataSize() {
+ byte[] packetBytes = startServiceACK.constructPacket();
+
+ SdlPsm sdlPsm = new SdlPsm();
+ boolean didTransition = false;
+
+ for (byte packetByte : packetBytes) {
+ int state = sdlPsm.getState();
+ switch (state) {
+ case SdlPsm.MESSAGE_4_STATE:
+ didTransition = sdlPsm.handleByte(packetByte);
+ assertFalse(didTransition);
+ assertEquals(SdlPsm.ERROR_STATE, sdlPsm.getState());
+ return;
+ case SdlPsm.DATA_SIZE_1_STATE:
+ case SdlPsm.DATA_SIZE_2_STATE:
+ case SdlPsm.DATA_SIZE_3_STATE:
+ case SdlPsm.DATA_SIZE_4_STATE:
+ didTransition = sdlPsm.handleByte((byte) 0xFF);
+ assertTrue(didTransition);
+ break;
+ default:
+ didTransition = sdlPsm.handleByte(packetByte);
+ assertTrue(didTransition);
+ }
+ }
+ }
+
+ public void testIncorrectVersion() {
+ SdlPacket startServiceACK = SdlPacketFactory.createStartSessionACK(SessionType.RPC, (byte) 0x01, (byte) 0x05, (byte) 0x06);
+ startServiceACK.putTag(ControlFrameTags.RPC.StartServiceACK.HASH_ID, "3bb34978fe3a");
+ startServiceACK.putTag(ControlFrameTags.RPC.StartServiceACK.MTU, "150000");
+ startServiceACK.putTag(ControlFrameTags.RPC.StartServiceACK.PROTOCOL_VERSION, "5.1.0");
+ byte[] packetBytes = startServiceACK.constructPacket();
+
+ SdlPsm sdlPsm = new SdlPsm();
+ boolean didTransition = sdlPsm.handleByte(packetBytes[0]);
+ assertFalse(didTransition);
+ }
+
+ public void testIncorrectService() {
+
+ byte[] packetBytes = startServiceACK.constructPacket();
+
+ SdlPsm sdlPsm = new SdlPsm();
+ boolean didTransition = false;
+
+ for (byte packetByte : packetBytes) {
+ int state = sdlPsm.getState();
+ switch (state) {
+ case SdlPsm.SERVICE_TYPE_STATE:
+ didTransition = sdlPsm.handleByte((byte) 0xFF);
+ assertFalse(didTransition);
+ assertEquals(SdlPsm.ERROR_STATE, sdlPsm.getState());
+ return;
+ default:
+ didTransition = sdlPsm.handleByte(packetByte);
+ assertTrue(didTransition);
+ }
+ }
+ }
+
+ public void testRecovery() {
+ byte[] packetBytes = startServiceACK.constructPacket();
+ byte[] processingBytes = new byte[packetBytes.length + 15];
+
+ System.arraycopy(packetBytes, 10, processingBytes, 0, 15);
+ System.arraycopy(packetBytes, 0, processingBytes, 15, packetBytes.length);
+
+
+ SdlPsm sdlPsm = new SdlPsm();
+ boolean didTransition = false;
+ byte packetByte;
+ int state = SdlPsm.START_STATE, i = 0, limit = 0;
+
+ while (state != SdlPsm.FINISHED_STATE && limit < 10) {
+
+ packetByte = processingBytes[i];
+ didTransition = sdlPsm.handleByte(packetByte);
+ state = sdlPsm.getState();
+ if (!didTransition) {
+ assertEquals(SdlPsm.ERROR_STATE, state);
+ sdlPsm.reset();
+ } else if (state == SdlPsm.FINISHED_STATE) {
+ break;
+ }
+
+ if (i == processingBytes.length - 1) {
+ i = 0;
+ limit++;
+ } else {
+ i++;
+ }
+ }
+
+ assertEquals(SdlPsm.FINISHED_STATE, sdlPsm.getState());
+ SdlPacket parsedPacket = sdlPsm.getFormedPacket();
+ assertNotNull(parsedPacket);
+
+ }
+
+
protected void tearDown() throws Exception {
super.tearDown();
}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java
index 66894df8b..6f0e68aba 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java
@@ -87,9 +87,10 @@ public class LockScreenManager extends BaseSubManager {
final int customView;
int displayMode;
Bitmap deviceLogo;
- private boolean mLockScreenHasBeenDismissed, lockscreenDismissReceiverRegistered, receivedFirstDDNotification;
+ private boolean lockscreenDismissReceiverRegistered, receivedFirstDDNotification;
+ boolean mLockScreenShouldBeAutoDismissed;
private String mLockscreenWarningMsg;
- private BroadcastReceiver mLockscreenDismissedReceiver;
+ BroadcastReceiver mLockscreenDismissedReceiver;
private final LockScreenDeviceIconManager mLockScreenDeviceIconManager;
private String lastIntentUsed;
@@ -220,6 +221,11 @@ public class LockScreenManager extends BaseSubManager {
// enable the dismissal. There is a delay added to allow time for the activity
// time to completely start and handle the new intent. There seems to be odd behavior
// in Android when startActivity is called multiple times too quickly.
+ if (previousDismissibleState) {
+ // If lockscreen was dismissible, got dismissed by the user, then became not dismissible, the lockscreen activity should be allowed to launch again
+ // https://github.com/smartdevicelink/sdl_java_suite/issues/1695
+ mLockScreenShouldBeAutoDismissed = false;
+ }
if (!receivedFirstDDNotification) {
new Handler().postDelayed(new Runnable() {
@Override
@@ -296,7 +302,7 @@ public class LockScreenManager extends BaseSubManager {
@Override
public void onReceive(Context context, Intent intent) {
if (SDLLockScreenActivity.KEY_LOCKSCREEN_DISMISSED.equals(intent.getAction())) {
- mLockScreenHasBeenDismissed = true;
+ mLockScreenShouldBeAutoDismissed = true;
lastIntentUsed = null;
}
}
@@ -318,7 +324,7 @@ public class LockScreenManager extends BaseSubManager {
private void launchLockScreenActivity() {
synchronized (LOCKSCREEN_LAUNCH_LOCK) {
// If the user has dismissed the lockscreen for this run or has disabled it, do not show it
- if (mLockScreenHasBeenDismissed || displayMode == LockScreenConfig.DISPLAY_MODE_NEVER) {
+ if (mLockScreenShouldBeAutoDismissed || displayMode == LockScreenConfig.DISPLAY_MODE_NEVER) {
return;
}
// intent to open SDLLockScreenActivity
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/streaming/StreamPacketizer.java b/android/sdl_android/src/main/java/com/smartdevicelink/streaming/StreamPacketizer.java
index 097afd089..5bb682741 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/streaming/StreamPacketizer.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/streaming/StreamPacketizer.java
@@ -86,7 +86,7 @@ public class StreamPacketizer extends AbstractPacketizer implements IVideoStream
bufferSize = BUFF_READ_SIZE;
buffer = new byte[bufferSize];
}
- mOutputQueue = new LinkedBlockingQueue<>(MAX_QUEUE_SIZE / bufferSize);
+ mOutputQueue = new LinkedBlockingQueue<>(Math.max(MAX_QUEUE_SIZE / bufferSize, 1));
}
public void start() throws IOException {
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java
index 6f02088cc..fba01a561 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java
@@ -77,7 +77,8 @@ public class MultiplexBluetoothTransport extends MultiplexBaseTransport {
Handler timeOutHandler;
Runnable socketRunnable;
boolean keepSocketAlive = true;
-
+ BluetoothDevice connectedDevice;
+
/**
* Constructor. Prepares a new BluetoothChat session.
*
@@ -113,6 +114,14 @@ public class MultiplexBluetoothTransport extends MultiplexBaseTransport {
}
/**
+ * A method to retrieve the currently connected bluetooth device
+ * @return the connected bluetooth device if connected, null otherwise.
+ */
+ public BluetoothDevice getConnectedDevice(){
+ return connectedDevice;
+ }
+
+ /**
* Start the chat service. Specifically start AcceptThread to begin a
* session in listening (server) mode. Called by the Activity onResume()
*/
@@ -225,6 +234,7 @@ public class MultiplexBluetoothTransport extends MultiplexBaseTransport {
//Store a static name of the device that is connected.
if (device != null) {
+ connectedDevice = device;
connectedDeviceName = device.getName();
connectedDeviceAddress = device.getAddress();
if (connectedDeviceAddress != null) {
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java
index 605200ce5..c86cb6d94 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java
@@ -275,9 +275,16 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver {
DebugTool.logInfo(TAG, ": This app's package: " + myPackage);
DebugTool.logInfo(TAG, ": Router service app's package: " + routerServicePackage);
if (myPackage != null && myPackage.equalsIgnoreCase(routerServicePackage)) {
- SdlDeviceListener sdlDeviceListener = getSdlDeviceListener(context, device);
- if (!sdlDeviceListener.isRunning()) {
- sdlDeviceListener.start();
+ //If the device is not null the listener should start as well as the
+ //case where this app was installed after BT connected and is the
+ //only SDL app installed on the device. (Rare corner case)
+ if(device != null || sdlAppInfoList.size() == 1) {
+ SdlDeviceListener sdlDeviceListener = getSdlDeviceListener(context, device);
+ if (!sdlDeviceListener.isRunning()) {
+ sdlDeviceListener.start();
+ }
+ } else {
+ DebugTool.logInfo(TAG, "Not starting device listener, bluetooth device is null and other SDL apps installed.");
}
} else {
DebugTool.logInfo(TAG, ": Not the app to start the router service nor device listener");
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportManager.java
index 8dbbe1eef..a1392aace 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportManager.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportManager.java
@@ -119,8 +119,7 @@ public class TransportManager extends TransportManagerBase {
transport.removeSession(sessionId);
transport.stop();
} else if (legacyBluetoothTransport != null) {
- legacyBluetoothTransport.stop();
- legacyBluetoothTransport = null;
+ exitLegacyMode(null);
}
}
@@ -426,25 +425,32 @@ public class TransportManager extends TransportManagerBase {
}
}
+ /**
+ * A synchronized method to exit out of legacy mode
+ * @param info if this is null, the transport listener callback will not be called
+ */
@Override
synchronized void exitLegacyMode(String info) {
- TransportRecord legacyTransportRecord = null;
- if (legacyBluetoothTransport != null) {
- legacyTransportRecord = legacyBluetoothTransport.getTransportRecord();
+ if (legacyBluetoothTransport != null) { //If this is null, there is no reason to proceed
+ TransportRecord legacyTransportRecord = legacyBluetoothTransport.getTransportRecord();
legacyBluetoothTransport.stop();
legacyBluetoothTransport = null;
- }
- legacyBluetoothHandler = null;
- synchronized (TRANSPORT_STATUS_LOCK) {
- TransportManager.this.transportStatus.clear();
- }
- if (contextWeakReference != null) {
- try {
- contextWeakReference.get().unregisterReceiver(legacyDisconnectReceiver);
- } catch (Exception e) {
+
+ legacyBluetoothHandler = null;
+ synchronized (TRANSPORT_STATUS_LOCK) {
+ TransportManager.this.transportStatus.clear();
+ }
+ if (contextWeakReference != null) {
+ try {
+ contextWeakReference.get().unregisterReceiver(legacyDisconnectReceiver);
+ } catch (Exception e) {
+ DebugTool.logError(TAG, "Error attempting to unregister legacy mode receiver", e);
+ }
+ }
+ if (transportListener != null && info != null) {
+ transportListener.onTransportDisconnected(info, legacyTransportRecord, null);
}
}
- transportListener.onTransportDisconnected(info, legacyTransportRecord, null);
}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/SdlDeviceListener.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/SdlDeviceListener.java
index 251eb130f..23cdd9857 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/SdlDeviceListener.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/SdlDeviceListener.java
@@ -61,7 +61,7 @@ public class SdlDeviceListener {
private final WeakReference<Context> contextWeakReference;
private final Callback callback;
- private final BluetoothDevice connectedDevice;
+ private BluetoothDevice connectedDevice;
private MultiplexBluetoothTransport bluetoothTransport;
private TransportHandler bluetoothHandler;
private Handler timeoutHandler;
@@ -87,24 +87,21 @@ public class SdlDeviceListener {
public void start() {
if (connectedDevice == null) {
DebugTool.logInfo(TAG, ": No supplied bluetooth device");
- if (callback != null) {
- callback.onTransportError(null);
- }
- return;
- }
-
- if (hasSDLConnected(contextWeakReference.get(), connectedDevice.getAddress())) {
+ } else if (hasSDLConnected(contextWeakReference.get(), connectedDevice.getAddress())) {
DebugTool.logInfo(TAG, ": Confirmed SDL device, should start router service");
//This device has connected to SDL previously, it is ok to start the RS right now
callback.onTransportConnected(contextWeakReference.get(), connectedDevice);
return;
}
+
synchronized (RUNNING_LOCK) {
isRunning = true;
// set timeout = if first time seeing BT device, 30s, if not 15s
- int timeout = isFirstStatusCheck(connectedDevice.getAddress()) ? 30000 : 15000;
+ int timeout = connectedDevice != null && isFirstStatusCheck(connectedDevice.getAddress()) ? 30000 : 15000;
//Set our preference as false for this device for now
- setSDLConnectedStatus(contextWeakReference.get(), connectedDevice.getAddress(), false);
+ if(connectedDevice != null) {
+ setSDLConnectedStatus(contextWeakReference.get(), connectedDevice.getAddress(), false);
+ }
bluetoothHandler = new TransportHandler(this);
bluetoothTransport = new MultiplexBluetoothTransport(bluetoothHandler);
bluetoothTransport.start();
@@ -155,6 +152,9 @@ public class SdlDeviceListener {
case SdlRouterService.MESSAGE_STATE_CHANGE:
switch (msg.arg1) {
case MultiplexBaseTransport.STATE_CONNECTED:
+ if(sdlListener.connectedDevice == null) {
+ sdlListener.connectedDevice = sdlListener.bluetoothTransport.getConnectedDevice();
+ }
sdlListener.setSDLConnectedStatus(sdlListener.contextWeakReference.get(), sdlListener.connectedDevice.getAddress(), true);
boolean keepConnectionOpen = sdlListener.callback.onTransportConnected(sdlListener.contextWeakReference.get(), sdlListener.connectedDevice);
if (!keepConnectionOpen) {
diff --git a/base/src/main/java/com/smartdevicelink/exception/SdlException.java b/base/src/main/java/com/smartdevicelink/exception/SdlException.java
index e55c80334..e6afa1c88 100644
--- a/base/src/main/java/com/smartdevicelink/exception/SdlException.java
+++ b/base/src/main/java/com/smartdevicelink/exception/SdlException.java
@@ -71,7 +71,6 @@ public class SdlException extends Exception {
}
if (detail != null) {
ret += "\nnested: " + detail.toString();
- detail.printStackTrace();
}
return ret;
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java b/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java
index 24b0d8455..db775e10e 100644
--- a/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java
@@ -205,7 +205,7 @@ abstract class BaseSdlManager {
try {
DebugTool.logInfo(TAG, response.serializeJSON().toString());
} catch (JSONException e) {
- e.printStackTrace();
+ DebugTool.logError(TAG, "Error attempting to serialize ChangeRegistrationResponse", e);
}
// go through and change sdlManager properties that were changed via the LCU update
diff --git a/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java b/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java
index d0b72eb1f..9412fb122 100644
--- a/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java
@@ -271,11 +271,17 @@ abstract class BaseFileManager extends BaseSubManager {
*/
public boolean hasUploadedFile(@NonNull SdlFile file) {
// HAX: [#827](https://github.com/smartdevicelink/sdl_ios/issues/827) Older versions of Core had a bug where list files would cache incorrectly.
- if (file.isPersistent() && mutableRemoteFileNames != null && mutableRemoteFileNames.contains(file.getName())) {
- // If it's a persistent file, the bug won't present itself; just check if it's on the remote system
- return true;
- } else if (!file.isPersistent() && mutableRemoteFileNames != null && mutableRemoteFileNames.contains(file.getName()) && uploadedEphemeralFileNames.contains(file.getName())) {
- // If it's an ephemeral file, the bug will present itself; check that it's a remote file AND that we've uploaded it this session
+ Version rpcVersion = new Version(internalInterface.getSdlMsgVersion());
+ if (new Version(4, 4, 0).isNewerThan(rpcVersion) == 1) {
+ if (file.isPersistent() && mutableRemoteFileNames != null && mutableRemoteFileNames.contains(file.getName())) {
+ // HAX: If it's a persistent file, the bug won't present itself; just check if it's on the remote system
+ return true;
+ } else if (!file.isPersistent() && mutableRemoteFileNames != null && mutableRemoteFileNames.contains(file.getName()) && uploadedEphemeralFileNames.contains(file.getName())) {
+ // HAX: If it's an ephemeral file, the bug will present itself; check that it's a remote file AND that we've uploaded it this session
+ return true;
+ }
+ } else if (mutableRemoteFileNames != null && mutableRemoteFileNames.contains(file.getName())) {
+ // If not connected to a system where the bug presents itself, we can trust the `remoteFileNames`
return true;
}
return false;
diff --git a/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java b/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java
index 820311a58..4a9b785c6 100644
--- a/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java
+++ b/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java
@@ -43,6 +43,7 @@ import com.smartdevicelink.proxy.RPCResponse;
import com.smartdevicelink.proxy.rpc.PutFile;
import com.smartdevicelink.proxy.rpc.PutFileResponse;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
+import com.smartdevicelink.util.DebugTool;
import java.io.IOException;
import java.io.InputStream;
@@ -221,7 +222,7 @@ class UploadFileOperation extends Task {
try {
this.inputStream.close();
} catch (IOException e) {
- e.printStackTrace();
+ DebugTool.logError(TAG,"Error attempting to close input stream", e);
}
}
@@ -330,7 +331,7 @@ class UploadFileOperation extends Task {
try {
bytesRead = inputStream.read(buffer, 0, size);
} catch (IOException e) {
- e.printStackTrace();
+ DebugTool.logError(TAG,"Error attempting to read from input stream", e);
}
if (bytesRead > 0) {
@@ -365,7 +366,7 @@ class UploadFileOperation extends Task {
try {
size = inputStream.available();
} catch (IOException e) {
- e.printStackTrace();
+ DebugTool.logError(TAG,"Error trying to get input stream size", e);
}
}
return size;
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 57cf59ff8..9cf72f7cd 100644
--- a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java
@@ -52,6 +52,7 @@ import com.smartdevicelink.proxy.RPCMessage;
import com.smartdevicelink.proxy.RPCNotification;
import com.smartdevicelink.proxy.RPCRequest;
import com.smartdevicelink.proxy.RPCResponse;
+import com.smartdevicelink.proxy.rpc.DisplayCapabilities;
import com.smartdevicelink.proxy.rpc.GenericResponse;
import com.smartdevicelink.proxy.rpc.OnAppInterfaceUnregistered;
import com.smartdevicelink.proxy.rpc.OnButtonEvent;
@@ -61,6 +62,8 @@ import com.smartdevicelink.proxy.rpc.OnSystemRequest;
import com.smartdevicelink.proxy.rpc.RegisterAppInterface;
import com.smartdevicelink.proxy.rpc.RegisterAppInterfaceResponse;
import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
+import com.smartdevicelink.proxy.rpc.SetDisplayLayout;
+import com.smartdevicelink.proxy.rpc.SetDisplayLayoutResponse;
import com.smartdevicelink.proxy.rpc.SubscribeButton;
import com.smartdevicelink.proxy.rpc.SystemRequest;
import com.smartdevicelink.proxy.rpc.TTSChunk;
@@ -73,6 +76,7 @@ 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.Language;
+import com.smartdevicelink.proxy.rpc.enums.PredefinedLayout;
import com.smartdevicelink.proxy.rpc.enums.RequestType;
import com.smartdevicelink.proxy.rpc.enums.Result;
import com.smartdevicelink.proxy.rpc.enums.SdlDisconnectedReason;
@@ -132,6 +136,8 @@ abstract class BaseLifecycleManager {
BaseTransportConfig _transportConfig;
private Taskmaster taskmaster;
private boolean didCheckSystemInfo = false;
+ String lastDisplayLayoutRequestTemplate;
+ DisplayCapabilities initialMediaCapabilities;
BaseLifecycleManager(AppConfig appConfig, BaseTransportConfig config, LifecycleListener listener) {
this.appConfig = appConfig;
@@ -146,7 +152,7 @@ abstract class BaseLifecycleManager {
try {
session.startSession();
} catch (SdlException e) {
- e.printStackTrace();
+ DebugTool.logError(TAG,"Error attempting to start session", e);
}
}
@@ -384,6 +390,7 @@ abstract class BaseLifecycleManager {
msg.setCorrelationID(UNREGISTER_APP_INTERFACE_CORRELATION_ID);
sendRPCMessagePrivate(msg, true);
clean();
+ onClose("RPC spec version not supported: " + rpcSpecVersion.toString(), null, SdlDisconnectedReason.MINIMUM_RPC_VERSION_HIGHER_THAN_SUPPORTED);
return;
}
if (!didCheckSystemInfo && lifecycleListener != null) {
@@ -399,12 +406,17 @@ abstract class BaseLifecycleManager {
msg.setCorrelationID(UNREGISTER_APP_INTERFACE_CORRELATION_ID);
sendRPCMessagePrivate(msg, true);
clean();
+ onClose("System not supported", null, SdlDisconnectedReason.DEFAULT);
return;
}
}
//If the vehicle is acceptable and this is the first check, init security lib
setSecurityLibraryIfAvailable(vehicleType);
}
+ // HAX: Issue #1690, Ford Sync bug returning incorrect display capabilities (https://github.com/smartdevicelink/sdl_java_suite/issues/1690). Store the initial capabilities if we are a media app so that we can use them in the future.
+ if (appConfig.appType.contains(AppHMIType.MEDIA)) {
+ initialMediaCapabilities = raiResponse.getDisplayCapabilities();
+ }
systemCapabilityManager.parseRAIResponse(raiResponse);
break;
case ON_HMI_STATUS:
@@ -481,6 +493,7 @@ abstract class BaseLifecycleManager {
if (!onAppInterfaceUnregistered.getReason().equals(AppInterfaceUnregisteredReason.LANGUAGE_CHANGE)) {
DebugTool.logInfo(TAG, "on app interface unregistered");
clean();
+ onClose("OnAppInterfaceUnregistered received from head unit", null, SdlDisconnectedReason.APP_INTERFACE_UNREG);
} else {
DebugTool.logInfo(TAG, "re-registering for language change");
cycle(SdlDisconnectedReason.LANGUAGE_CHANGE);
@@ -489,6 +502,7 @@ abstract class BaseLifecycleManager {
case UNREGISTER_APP_INTERFACE:
DebugTool.logInfo(TAG, "unregister app interface");
clean();
+ onClose("UnregisterAppInterface response received from head unit", null, SdlDisconnectedReason.APP_INTERFACE_UNREG);
break;
}
}
@@ -825,6 +839,10 @@ abstract class BaseLifecycleManager {
addOnRPCResponseListener(listener, corrId);
}
}
+ // HAX: Issue #1690, Ford Sync bug returning incorrect display capabilities (https://github.com/smartdevicelink/sdl_java_suite/issues/1690). Save the next desired layout type to the update capabilities when the SetDisplayLayout response is received
+ if (FunctionID.SET_DISPLAY_LAYOUT.toString().equals(message.getFunctionName())) {
+ lastDisplayLayoutRequestTemplate = ((SetDisplayLayout)message).getDisplayLayout();
+ }
} else if (RPCMessage.KEY_RESPONSE.equals(message.getMessageType())) { // Response Specifics
RPCResponse response = (RPCResponse) message;
pm.setRPCType((byte) 0x01);
@@ -851,7 +869,17 @@ abstract class BaseLifecycleManager {
session.sendMessage(pm);
} catch (OutOfMemoryError e) {
- e.printStackTrace();
+ DebugTool.logError(TAG,"Error attempting to send RPC message.", e);
+ }
+ }
+
+ // HAX: Issue #1690, Ford Sync bug returning incorrect display capabilities (https://github.com/smartdevicelink/sdl_java_suite/issues/1690). Use the initial capabilities from RAIR instead of the incorrect ones that are included in SetDisplayLayoutResponse.
+ void fixIncorrectDisplayCapabilities(RPCMessage rpc) {
+ if (RPCMessage.KEY_RESPONSE.equals(rpc.getMessageType()) && rpc.getFunctionName().equals(FunctionID.SET_DISPLAY_LAYOUT.toString()) &&
+ initialMediaCapabilities != null && PredefinedLayout.MEDIA.toString().equals(lastDisplayLayoutRequestTemplate)) {
+
+ SetDisplayLayoutResponse setDisplayLayoutResponse = (SetDisplayLayoutResponse) rpc;
+ setDisplayLayoutResponse.setDisplayCapabilities(initialMediaCapabilities);
}
}
@@ -875,6 +903,8 @@ abstract class BaseLifecycleManager {
rpc.format(rpcSpecVersion, true);
+ fixIncorrectDisplayCapabilities(rpc);
+
BaseLifecycleManager.this.onRPCReceived(rpc);
if (RPCMessage.KEY_RESPONSE.equals(messageType)) {
@@ -910,6 +940,7 @@ abstract class BaseLifecycleManager {
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.RPC);
clean();
+ onClose("Protocol version not supported: " + version, null, SdlDisconnectedReason.MINIMUM_PROTOCOL_VERSION_HIGHER_THAN_SUPPORTED);
return;
}
@@ -920,6 +951,7 @@ abstract class BaseLifecycleManager {
DebugTool.logWarning(TAG, "Disconnecting from head unit, the system info was not accepted.");
session.endService(SessionType.RPC);
clean();
+ onClose("System not supported", null, SdlDisconnectedReason.DEFAULT);
return;
}
//If the vehicle is acceptable, init security lib
@@ -1197,6 +1229,8 @@ abstract class BaseLifecycleManager {
void clean() {
firstTimeFull = true;
currentHMIStatus = null;
+ lastDisplayLayoutRequestTemplate = null;
+ initialMediaCapabilities = null;
if (rpcListeners != null) {
rpcListeners.clear();
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java
index cfb2176df..54a343043 100644
--- a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java
@@ -133,8 +133,16 @@ abstract class BaseSystemCapabilityManager {
return Collections.singletonList(displayCapability);
}
+ // HAX: Issue #1705, Ford Sync bug returning incorrect template name for "NON-MEDIA" (https://github.com/smartdevicelink/sdl_java_suite/issues/1705).
+ List<String> templatesAvailable = display.getTemplatesAvailable();
+ for (int i = 0; i < templatesAvailable.size(); i++) {
+ if (templatesAvailable.get(i).equals("NON_MEDIA")) {
+ templatesAvailable.set(i, "NON-MEDIA");
+ break;
+ }
+ }
// copy all available display capabilities
- defaultWindowCapability.setTemplatesAvailable(display.getTemplatesAvailable());
+ defaultWindowCapability.setTemplatesAvailable(templatesAvailable);
defaultWindowCapability.setNumCustomPresetsAvailable(display.getNumCustomPresetsAvailable());
defaultWindowCapability.setTextFields(display.getTextFields());
defaultWindowCapability.setImageFields(display.getImageFields());
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/BaseChoiceSetManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/BaseChoiceSetManager.java
index 746d49865..893909718 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/BaseChoiceSetManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/BaseChoiceSetManager.java
@@ -42,6 +42,7 @@ import com.livio.taskmaster.Task;
import com.smartdevicelink.managers.BaseSubManager;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.managers.ManagerUtility;
import com.smartdevicelink.managers.file.FileManager;
import com.smartdevicelink.managers.lifecycle.OnSystemCapabilityListener;
import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager;
@@ -54,6 +55,7 @@ import com.smartdevicelink.proxy.rpc.KeyboardProperties;
import com.smartdevicelink.proxy.rpc.OnHMIStatus;
import com.smartdevicelink.proxy.rpc.WindowCapability;
import com.smartdevicelink.proxy.rpc.enums.HMILevel;
+import com.smartdevicelink.proxy.rpc.enums.ImageFieldName;
import com.smartdevicelink.proxy.rpc.enums.InteractionMode;
import com.smartdevicelink.proxy.rpc.enums.KeyboardLayout;
import com.smartdevicelink.proxy.rpc.enums.KeypressMode;
@@ -61,11 +63,13 @@ import com.smartdevicelink.proxy.rpc.enums.Language;
import com.smartdevicelink.proxy.rpc.enums.PredefinedWindows;
import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType;
import com.smartdevicelink.proxy.rpc.enums.SystemContext;
+import com.smartdevicelink.proxy.rpc.enums.TextFieldName;
import com.smartdevicelink.proxy.rpc.enums.TriggerSource;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
import com.smartdevicelink.util.DebugTool;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
@@ -540,7 +544,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
* E.g. Choices param contains 2 cells with text/title "Address" will be handled by updating the uniqueText/uniqueTitle of the second cell to "Address (2)".
* @param choices The list of choiceCells to be uploaded.
*/
- void addUniqueNamesToCells(LinkedHashSet<ChoiceCell> choices) {
+ void addUniqueNamesToCells(List<ChoiceCell> choices) {
HashMap<String, Integer> dictCounter = new HashMap<>();
for (ChoiceCell cell : choices) {
@@ -556,16 +560,80 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
}
}
+ void addUniqueNamesBasedOnStrippedCells(List<ChoiceCell> strippedCells, List<ChoiceCell> unstrippedCells) {
+ if (strippedCells == null || unstrippedCells == null || strippedCells.size() != unstrippedCells.size()) {
+ return;
+ }
+ // Tracks how many of each cell primary text there are so that we can append numbers to make each unique as necessary
+ HashMap<ChoiceCell, Integer> dictCounter = new HashMap<>();
+ for (int i = 0; i < strippedCells.size(); i++) {
+ ChoiceCell cell = strippedCells.get(i);
+ Integer counter = dictCounter.get(cell);
+ if (counter != null) {
+ counter++;
+ dictCounter.put(cell, counter);
+ } else {
+ dictCounter.put(cell, 1);
+ }
+
+ counter = dictCounter.get(cell);
+
+ if (counter > 1) {
+ unstrippedCells.get(i).setUniqueText(unstrippedCells.get(i).getText() + " (" + counter + ")");
+ }
+ }
+ }
+
+ private List<ChoiceCell> cloneChoiceCellList(List<ChoiceCell> originalList) {
+ if (originalList == null) {
+ return null;
+ }
+
+ List<ChoiceCell> clone = new ArrayList<>();
+ for (ChoiceCell choiceCell : originalList) {
+ clone.add(choiceCell.clone());
+ }
+ return clone;
+ }
+
private LinkedHashSet<ChoiceCell> getChoicesToBeUploadedWithArray(List<ChoiceCell> choices) {
- LinkedHashSet<ChoiceCell> choiceSet = new LinkedHashSet<>(choices);
- // If we're running on a connection < RPC 7.1, we need to de-duplicate cells because presenting them will fail if we have the same cell primary text.
+ // Clone choices
+ List<ChoiceCell> choicesClone = cloneChoiceCellList(choices);
if (choices != null && internalInterface.getSdlMsgVersion() != null
&& (internalInterface.getSdlMsgVersion().getMajorVersion() < 7
|| (internalInterface.getSdlMsgVersion().getMajorVersion() == 7 && internalInterface.getSdlMsgVersion().getMinorVersion() == 0))) {
- addUniqueNamesToCells(choiceSet);
+ // If we're on < RPC 7.1, all primary texts need to be unique, so we don't need to check removed properties and duplicate cells
+ addUniqueNamesToCells(choicesClone);
+ } else {
+ List<ChoiceCell> strippedCellsClone = removeUnusedProperties(choicesClone);
+ addUniqueNamesBasedOnStrippedCells(strippedCellsClone, choicesClone);
}
- choiceSet.removeAll(preloadedChoices);
- return choiceSet;
+ LinkedHashSet<ChoiceCell> choiceCloneLinkedHash = new LinkedHashSet<>(choicesClone);
+ choiceCloneLinkedHash.removeAll(preloadedChoices);
+ return choiceCloneLinkedHash;
+ }
+
+ List<ChoiceCell> removeUnusedProperties(List<ChoiceCell> choiceCells) {
+ List<ChoiceCell> strippedCellsClone = cloneChoiceCellList(choiceCells);
+ //Clone Cells
+ for (ChoiceCell cell : strippedCellsClone) {
+ // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI
+ cell.setVoiceCommands(null);
+
+ if (!hasImageFieldOfName(ImageFieldName.choiceImage)) {
+ cell.setArtwork(null);
+ }
+ if (!hasTextFieldOfName(TextFieldName.secondaryText)) {
+ cell.setSecondaryText(null);
+ }
+ if (!hasTextFieldOfName(TextFieldName.tertiaryText)) {
+ cell.setTertiaryText(null);
+ }
+ if (!hasImageFieldOfName(ImageFieldName.choiceSecondaryImage)) {
+ cell.setSecondaryArtwork(null);
+ }
+ }
+ return strippedCellsClone;
}
void updateIdsOnChoices(LinkedHashSet<ChoiceCell> choices) {
@@ -672,6 +740,14 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
// ADDITIONAL HELPERS
+ private boolean hasImageFieldOfName(ImageFieldName imageFieldName) {
+ return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, imageFieldName);
+ }
+
+ private boolean hasTextFieldOfName(TextFieldName textFieldName) {
+ return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, textFieldName);
+ }
+
boolean setUpChoiceSet(ChoiceSet choiceSet) {
List<ChoiceCell> choices = choiceSet.getChoices();
@@ -695,9 +771,8 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
int choiceCellWithVoiceCommandCount = 0;
for (ChoiceCell cell : choices) {
-
uniqueChoiceCells.add(cell);
-
+ // Not using cloned cell here because we set the clone's VoiceCommands to null for visual check only
if (cell.getVoiceCommands() != null) {
uniqueVoiceCommands.addAll(cell.getVoiceCommands());
choiceCellWithVoiceCommandCount += 1;
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java
index f39423b1a..d19bae619 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java
@@ -176,11 +176,9 @@ abstract class BaseMenuManager extends BaseSubManager {
// Create a deep copy of the list so future changes by developers don't affect the algorithm logic
List<MenuCell> clonedCells = cloneMenuCellsList(cells);
- // If we're running on a connection < RPC 7.1, we need to de-duplicate cells because presenting them will fail if we have the same cell primary text.
- if (clonedCells != null && internalInterface.getSdlMsgVersion() != null
- && (internalInterface.getSdlMsgVersion().getMajorVersion() < 7
- || (internalInterface.getSdlMsgVersion().getMajorVersion() == 7 && internalInterface.getSdlMsgVersion().getMinorVersion() == 0))) {
- addUniqueNamesToCells(clonedCells);
+ // Check for cell lists with completely duplicate information, or any duplicate voiceCommands and return if it fails (logs are in the called method).
+ if (clonedCells != null && !menuCellsAreUnique(clonedCells, new ArrayList<String>())) {
+ return;
}
if (currentHMILevel == null || currentHMILevel.equals(HMILevel.HMI_NONE) || currentSystemContext.equals(SystemContext.SYSCTXT_MENU)) {
@@ -194,6 +192,23 @@ abstract class BaseMenuManager extends BaseSubManager {
}
waitingOnHMIUpdate = false;
+ // If we're running on a connection < RPC 7.1, we need to de-duplicate cells because presenting them will fail if we have the same cell primary text.
+ if (clonedCells != null && internalInterface.getSdlMsgVersion() != null
+ && (internalInterface.getSdlMsgVersion().getMajorVersion() < 7
+ || (internalInterface.getSdlMsgVersion().getMajorVersion() == 7 && internalInterface.getSdlMsgVersion().getMinorVersion() == 0))) {
+ addUniqueNamesToCellsWithDuplicatePrimaryText(clonedCells);
+ } else {
+ // On > RPC 7.1, at this point all cells are unique when considering all properties,
+ // but we also need to check if any cells will _appear_ as duplicates when displayed on the screen.
+ // To check that, we'll remove properties from the set cells based on the system capabilities
+ // (we probably don't need to consider them changing between now and when they're actually sent to the HU unless the menu layout changes)
+ // and check for uniqueness again. Then we'll add unique identifiers to primary text if there are duplicates.
+ // Then we transfer the primary text identifiers back to the main cells and add those to an operation to be sent.
+ List<MenuCell> strippedCellsClone = removeUnusedProperties(clonedCells);
+ addUniqueNamesBasedOnStrippedCells(strippedCellsClone, clonedCells);
+
+ }
+
// Update our Lists
// set old list
if (menuCells != null) {
@@ -205,11 +220,6 @@ abstract class BaseMenuManager extends BaseSubManager {
menuCells.addAll(clonedCells);
}
- // Check for cell lists with completely duplicate information, or any duplicate voiceCommands and return if it fails (logs are in the called method).
- if (!menuCellsAreUnique(menuCells, new ArrayList<String>())) {
- return;
- }
-
// Upload the Artworks
List<SdlArtwork> artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(menuCells);
if (artworksToBeUploaded.size() > 0 && fileManager.get() != null) {
@@ -1041,7 +1051,7 @@ abstract class BaseMenuManager extends BaseSubManager {
command.setVrCommands(null);
}
command.setCmdIcon((cell.getIcon() != null && shouldHaveArtwork) ? cell.getIcon().getImageRPC() : null);
- command.setSecondaryImage((cell.getSecondaryArtwork() != null && shouldHaveArtwork && !(fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getSecondaryArtwork()))) ? cell.getSecondaryArtwork().getImageRPC() : null);
+ command.setSecondaryImage((cell.getSecondaryArtwork() != null && shouldHaveArtwork && hasImageFieldOfName(ImageFieldName.menuCommandSecondaryImage) && !(fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getSecondaryArtwork()))) ? cell.getSecondaryArtwork().getImageRPC() : null);
return command;
}
@@ -1065,7 +1075,7 @@ abstract class BaseMenuManager extends BaseSubManager {
subMenu.setMenuLayout(menuConfiguration.getSubMenuLayout());
}
subMenu.setMenuIcon((shouldHaveArtwork && (cell.getIcon() != null && cell.getIcon().getImageRPC() != null)) ? cell.getIcon().getImageRPC() : null);
- subMenu.setSecondaryImage((shouldHaveArtwork && !(fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getSecondaryArtwork())) && (cell.getSecondaryArtwork() != null && cell.getSecondaryArtwork().getImageRPC() != null)) ? cell.getSecondaryArtwork().getImageRPC() : null);
+ subMenu.setSecondaryImage((shouldHaveArtwork && hasImageFieldOfName(ImageFieldName.menuSubMenuSecondaryImage) && !(fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getSecondaryArtwork())) && (cell.getSecondaryArtwork() != null && cell.getSecondaryArtwork().getImageRPC() != null)) ? cell.getSecondaryArtwork().getImageRPC() : null);
return subMenu;
}
@@ -1275,7 +1285,7 @@ abstract class BaseMenuManager extends BaseSubManager {
try {
DebugTool.logInfo(TAG, "Main Menu response: " + response.serializeJSON().toString());
} catch (JSONException e) {
- e.printStackTrace();
+ DebugTool.logError(TAG,"Error attempting to serialize JSON of RPC response", e);
}
} else {
DebugTool.logError(TAG, "Result: " + response.getResultCode() + " Info: " + response.getInfo());
@@ -1313,7 +1323,7 @@ abstract class BaseMenuManager extends BaseSubManager {
try {
DebugTool.logInfo(TAG, "Sub Menu response: " + response.serializeJSON().toString());
} catch (JSONException e) {
- e.printStackTrace();
+ DebugTool.logError(TAG,"Error attempting to serialize JSON of RPC response", e);
}
} else {
DebugTool.logError(TAG, "Failed to send sub menu commands: " + response.getInfo());
@@ -1365,7 +1375,7 @@ abstract class BaseMenuManager extends BaseSubManager {
try {
DebugTool.logInfo(TAG, "Dynamic Sub Menu response: " + response.serializeJSON().toString());
} catch (JSONException e) {
- e.printStackTrace();
+ DebugTool.logError(TAG,"Error attempting to serialize JSON of RPC response", e);
}
} else {
DebugTool.logError(TAG, "Result: " + response.getResultCode() + " Info: " + response.getInfo());
@@ -1441,7 +1451,7 @@ abstract class BaseMenuManager extends BaseSubManager {
return clone;
}
- private void addUniqueNamesToCells(List<MenuCell> cells) {
+ private void addUniqueNamesToCellsWithDuplicatePrimaryText(List<MenuCell> cells) {
HashMap<String, Integer> dictCounter = new HashMap<>();
for (MenuCell cell : cells) {
@@ -1456,11 +1466,80 @@ abstract class BaseMenuManager extends BaseSubManager {
}
if (cell.getSubCells() != null && cell.getSubCells().size() > 0) {
- addUniqueNamesToCells(cell.getSubCells());
+ addUniqueNamesToCellsWithDuplicatePrimaryText(cell.getSubCells());
}
}
}
+ void addUniqueNamesBasedOnStrippedCells(List<MenuCell> strippedCells, List<MenuCell> unstrippedCells) {
+ if (strippedCells == null || unstrippedCells == null || strippedCells.size() != unstrippedCells.size()) {
+ return;
+ }
+ // Tracks how many of each cell primary text there are so that we can append numbers to make each unique as necessary
+ HashMap<MenuCell, Integer> dictCounter = new HashMap<>();
+ for (int i = 0; i < strippedCells.size(); i++) {
+ MenuCell cell = strippedCells.get(i);
+ Integer counter = dictCounter.get(cell);
+ if (counter != null) {
+ counter = counter + 1;
+ dictCounter.put(cell, counter);
+ } else {
+ dictCounter.put(cell, 1);
+ }
+ counter = dictCounter.get(cell);
+ if (counter > 1) {
+ unstrippedCells.get(i).setUniqueTitle(unstrippedCells.get(i).getTitle() + " (" + counter + ")");
+ }
+
+ if (cell.getSubCells() != null && cell.getSubCells().size() > 0) {
+ addUniqueNamesBasedOnStrippedCells(cell.getSubCells(), unstrippedCells.get(i).getSubCells());
+ }
+
+ }
+
+
+ }
+
+ List<MenuCell> removeUnusedProperties(List<MenuCell> menuCells) {
+ if (menuCells == null) {
+ return null;
+ }
+ List<MenuCell> removePropertiesClone = cloneMenuCellsList(menuCells);
+ for (MenuCell cell : removePropertiesClone) {
+ // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI
+ cell.setVoiceCommands(null);
+
+ // Don't check ImageFieldName.subMenuIcon because it was added in 7.0 when the feature was added in 5.0.
+ // Just assume that if cmdIcon is not available, the submenu icon is not either.
+ if (!hasImageFieldOfName(ImageFieldName.cmdIcon)) {
+ cell.setIcon(null);
+ }
+ // Check for subMenu fields supported
+ if (cell.getSubCells() != null) {
+ if (!hasTextFieldOfName(TextFieldName.menuSubMenuSecondaryText)) {
+ cell.setSecondaryText(null);
+ }
+ if (!hasTextFieldOfName(TextFieldName.menuSubMenuTertiaryText)) {
+ cell.setTertiaryText(null);
+ }
+ if (!hasImageFieldOfName(ImageFieldName.menuSubMenuSecondaryImage)) {
+ cell.setSecondaryArtwork(null);
+ }
+ cell.setSubCells(removeUnusedProperties(cell.getSubCells()));
+ } else {
+ if (!hasTextFieldOfName(TextFieldName.menuCommandSecondaryText)) {
+ cell.setSecondaryText(null);
+ }
+ if (!hasTextFieldOfName(TextFieldName.menuCommandTertiaryText)) {
+ cell.setTertiaryText(null);
+ }
+ if (!hasImageFieldOfName(ImageFieldName.menuCommandSecondaryImage)) {
+ cell.setSecondaryArtwork(null);
+ }
+ }
+ }
+ return removePropertiesClone;
+ }
/**
* Check for cell lists with completely duplicate information, or any duplicate voiceCommands
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseVoiceCommandManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseVoiceCommandManager.java
index 3e5fa6dbe..9c5c39c21 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseVoiceCommandManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseVoiceCommandManager.java
@@ -51,6 +51,7 @@ import com.smartdevicelink.util.DebugTool;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
abstract class BaseVoiceCommandManager extends BaseSubManager {
@@ -131,16 +132,40 @@ abstract class BaseVoiceCommandManager extends BaseSubManager {
public void setVoiceCommands(List<VoiceCommand> voiceCommands) {
// we actually need voice commands to set.
- if (voiceCommands == null || voiceCommands.equals(this.voiceCommands)) {
- DebugTool.logInfo(TAG, "Voice commands list was null or matches the current voice commands");
+ if (voiceCommands == null) {
+ DebugTool.logInfo(TAG, "Voice commands list was null");
return;
}
- updateIdsOnVoiceCommands(voiceCommands);
- this.voiceCommands = new ArrayList<>(voiceCommands);
+ // Clone voice commands
+ this.voiceCommands = new ArrayList<>();
+ for (VoiceCommand voiceCommand : voiceCommands) {
+ if (voiceCommand == null) {
+ continue;
+ }
+ this.voiceCommands.add(voiceCommand.clone());
+ }
+
+ List<VoiceCommand> validatedVoiceCommands = removeEmptyVoiceCommands(this.voiceCommands);
+
+ if (validatedVoiceCommands.size() == 0 && voiceCommands.size() > 0) {
+ DebugTool.logError(TAG, "New voice commands are invalid, skipping...");
+ this.voiceCommands = null;
+ return;
+ }
+
+ if (!isVoiceCommandsUnique(validatedVoiceCommands)) {
+ DebugTool.logError(TAG, "Not all voice command strings are unique across all voice commands. Voice commands will not be set.");
+ this.voiceCommands = null;
+ return;
+ }
+
+ this.voiceCommands = validatedVoiceCommands;
+
+ updateIdsOnVoiceCommands(this.voiceCommands);
cleanTransactionQueue();
- updateOperation = new VoiceCommandUpdateOperation(internalInterface, currentVoiceCommands, voiceCommands, new VoiceCommandUpdateOperation.VoiceCommandChangesListener() {
+ updateOperation = new VoiceCommandUpdateOperation(internalInterface, currentVoiceCommands, this.voiceCommands, new VoiceCommandUpdateOperation.VoiceCommandChangesListener() {
@Override
public void updateVoiceCommands(List<VoiceCommand> newCurrentVoiceCommands, HashMap<RPCRequest, String> errorObject) {
DebugTool.logInfo(TAG, "The updated list of VoiceCommands: " + newCurrentVoiceCommands);
@@ -176,7 +201,7 @@ abstract class BaseVoiceCommandManager extends BaseSubManager {
continue;
}
VoiceCommandUpdateOperation vcOperation = (VoiceCommandUpdateOperation) operation;
- vcOperation.oldVoiceCommands = newCurrentVoiceCommands;
+ vcOperation.setOldVoiceCommands(newCurrentVoiceCommands);
}
}
@@ -188,6 +213,30 @@ abstract class BaseVoiceCommandManager extends BaseSubManager {
}
}
+ List<VoiceCommand> removeEmptyVoiceCommands(List<VoiceCommand> voiceCommands) {
+ List<VoiceCommand> validatedVoiceCommands = new ArrayList<>();
+ for (VoiceCommand voiceCommand : voiceCommands) {
+ if (voiceCommand == null) {
+ continue;
+ }
+ List<String> voiceCommandStrings = new ArrayList<>();
+ for (String voiceCommandString : voiceCommand.getVoiceCommands()) {
+ if (voiceCommandString == null) {
+ continue;
+ }
+ String trimmedString = voiceCommandString.trim();
+ if (trimmedString.length() > 0) {
+ voiceCommandStrings.add(trimmedString);
+ }
+ }
+ if (voiceCommandStrings.size() > 0) {
+ voiceCommand.setVoiceCommands(voiceCommandStrings);
+ validatedVoiceCommands.add(voiceCommand);
+ }
+ }
+ return validatedVoiceCommands;
+ }
+
// LISTENERS
private void addListeners() {
@@ -211,8 +260,8 @@ abstract class BaseVoiceCommandManager extends BaseSubManager {
@Override
public void onNotified(RPCNotification notification) {
OnCommand onCommand = (OnCommand) notification;
- if (voiceCommands != null && voiceCommands.size() > 0) {
- for (VoiceCommand command : voiceCommands) {
+ if (currentVoiceCommands != null && currentVoiceCommands.size() > 0) {
+ for (VoiceCommand command : currentVoiceCommands) {
if (onCommand.getCmdID() == command.getCommandId()) {
if (command.getVoiceCommandSelectionListener() != null) {
command.getVoiceCommandSelectionListener().onVoiceCommandSelected();
@@ -225,4 +274,24 @@ abstract class BaseVoiceCommandManager extends BaseSubManager {
};
internalInterface.addOnRPCNotificationListener(FunctionID.ON_COMMAND, commandListener);
}
+
+ /**
+ * Boolean method that checks to see if all VoiceCommands in a given list are unique
+ *
+ * @param voiceCommands - list of VoiceCommands
+ * @return - true if VoiceCommands are unique, false if not
+ */
+ private boolean isVoiceCommandsUnique(List<VoiceCommand> voiceCommands) {
+ HashSet<String> voiceCommandHashSet = new HashSet<>();
+ int voiceCommandCount = 0;
+
+ for (VoiceCommand voiceCommand : voiceCommands) {
+ if (voiceCommand == null) {
+ continue;
+ }
+ voiceCommandHashSet.addAll(voiceCommand.getVoiceCommands());
+ voiceCommandCount += voiceCommand.getVoiceCommands().size();
+ }
+ return (voiceCommandHashSet.size() == voiceCommandCount);
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommand.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommand.java
index 15d66b09e..b171a702d 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommand.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommand.java
@@ -36,9 +36,13 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.smartdevicelink.util.DebugTool;
+
+import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
-public class VoiceCommand {
+public class VoiceCommand implements Cloneable {
/**
* The strings the user can say to activate this voice command
@@ -77,7 +81,7 @@ public class VoiceCommand {
* @param voiceCommands - the list of commands to send to the head unit
*/
public void setVoiceCommands(@NonNull List<String> voiceCommands) {
- this.voiceCommands = voiceCommands;
+ this.voiceCommands = new ArrayList<>(removeDuplicateStrings(voiceCommands));
}
/**
@@ -126,6 +130,10 @@ public class VoiceCommand {
return commandId;
}
+ private HashSet<String> removeDuplicateStrings(List<String> voiceCommands) {
+ return new HashSet<>(voiceCommands);
+ }
+
/**
* Used to compile hashcode for VoiceCommand for use to compare in equals method
*
@@ -134,9 +142,8 @@ public class VoiceCommand {
@Override
public int hashCode() {
int result = 1;
- result += Integer.rotateLeft(getCommandId(), 1);
for (int i = 0; i < this.getVoiceCommands().size(); i++) {
- result += ((getVoiceCommands().get(i) == null) ? 0 : Integer.rotateLeft(getVoiceCommands().get(i).hashCode(), i + 2));
+ result += ((getVoiceCommands().get(i) == null) ? 0 : Integer.rotateLeft(getVoiceCommands().get(i).hashCode(), i + 1));
}
return result;
}
@@ -152,9 +159,27 @@ public class VoiceCommand {
if (o == null) return false;
// if this is the same memory address, it's the same
if (this == o) return true;
- // if this is not an instance of SoftButtonObject, not the same
+ // if this is not an instance of VoiceCommand, not the same
if (!(o instanceof VoiceCommand)) return false;
// return comparison
return hashCode() == o.hashCode();
}
+
+ /**
+ * Creates a deep copy of the object
+ *
+ * @return deep copy of the object, null if an exception occurred
+ */
+ @Override
+ public VoiceCommand clone() {
+ try {
+ VoiceCommand clone = (VoiceCommand) super.clone();
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ if (DebugTool.isDebugEnabled()) {
+ throw new RuntimeException("Clone not supported by super class");
+ }
+ }
+ return null;
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperation.java
index b755abd7d..59caffbdb 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperation.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperation.java
@@ -22,7 +22,7 @@ class VoiceCommandUpdateOperation extends Task {
private List<VoiceCommand> pendingVoiceCommands;
private List<DeleteCommand> deleteVoiceCommands;
private List<AddCommand> addCommandsToSend;
- private VoiceCommandChangesListener voiceCommandListener;
+ VoiceCommandChangesListener voiceCommandListener;
private List<VoiceCommand> currentVoiceCommands;
private HashMap<RPCRequest, String> errorObject;
@@ -53,6 +53,15 @@ class VoiceCommandUpdateOperation extends Task {
onFinished();
return;
}
+ // Check if a voiceCommand has already been uploaded and update its VoiceCommandSelectionListener to
+ // prevent calling the wrong listener in a case where a voice command was uploaded and then its voiceCommandSelectionListener was updated in another upload.
+ if (pendingVoiceCommands != null && pendingVoiceCommands.size() > 0) {
+ for (VoiceCommand voiceCommand : pendingVoiceCommands) {
+ if (currentVoiceCommands.contains(voiceCommand)) {
+ currentVoiceCommands.get(currentVoiceCommands.indexOf(voiceCommand)).setVoiceCommandSelectionListener(voiceCommand.getVoiceCommandSelectionListener());
+ }
+ }
+ }
sendDeleteCurrentVoiceCommands(new CompletionListener() {
@Override
@@ -82,14 +91,23 @@ class VoiceCommandUpdateOperation extends Task {
private void sendDeleteCurrentVoiceCommands(final CompletionListener listener) {
- if (oldVoiceCommands == null || oldVoiceCommands.isEmpty()) {
+ if (oldVoiceCommands == null || oldVoiceCommands.size() == 0) {
if (listener != null) {
listener.onComplete(true);
}
return;
}
- deleteVoiceCommands = deleteCommandsForVoiceCommands(oldVoiceCommands);
+ List<VoiceCommand> voiceCommandsToDelete = voiceCommandsInListNotInSecondList(oldVoiceCommands, pendingVoiceCommands);
+
+ if (voiceCommandsToDelete.size() == 0) {
+ if (listener != null) {
+ listener.onComplete(true);
+ }
+ return;
+ }
+
+ deleteVoiceCommands = deleteCommandsForVoiceCommands(voiceCommandsToDelete);
internalInterface.get().sendRPCs(deleteVoiceCommands, new OnMultipleRequestListener() {
@Override
@@ -156,14 +174,16 @@ class VoiceCommandUpdateOperation extends Task {
private void sendCurrentVoiceCommands(final CompletionListener listener) {
- if (pendingVoiceCommands == null || pendingVoiceCommands.size() == 0) {
+ List<VoiceCommand> voiceCommandsToAdd = voiceCommandsInListNotInSecondList(pendingVoiceCommands, oldVoiceCommands);
+
+ if (voiceCommandsToAdd.size() == 0) {
if (listener != null) {
- listener.onComplete(true); // no voice commands to send doesnt mean that its an error
+ listener.onComplete(true); // no voice commands to send doesn't mean that its an error
}
return;
}
- addCommandsToSend = addCommandsForVoiceCommands(pendingVoiceCommands);
+ addCommandsToSend = addCommandsForVoiceCommands(voiceCommandsToAdd);
internalInterface.get().sendRPCs(addCommandsToSend, new OnMultipleRequestListener() {
@Override
@@ -207,6 +227,20 @@ class VoiceCommandUpdateOperation extends Task {
});
}
+ List<VoiceCommand> voiceCommandsInListNotInSecondList(List<VoiceCommand> firstList, List<VoiceCommand> secondList) {
+ if (secondList == null || secondList.size() == 0) {
+ return firstList;
+ }
+ List<VoiceCommand> differencesList = new ArrayList<>(firstList);
+ differencesList.removeAll(secondList);
+ return differencesList;
+ }
+
+ public void setOldVoiceCommands(List<VoiceCommand> oldVoiceCommands) {
+ this.oldVoiceCommands = oldVoiceCommands;
+ this.currentVoiceCommands = new ArrayList<>(oldVoiceCommands);
+ }
+
// Create AddCommand List
List<AddCommand> addCommandsForVoiceCommands(List<VoiceCommand> voiceCommands) {
diff --git a/base/src/main/java/com/smartdevicelink/proxy/RPCStruct.java b/base/src/main/java/com/smartdevicelink/proxy/RPCStruct.java
index c0dd495dd..1db39f2e9 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/RPCStruct.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/RPCStruct.java
@@ -50,6 +50,8 @@ import java.util.List;
import java.util.Set;
public class RPCStruct implements Cloneable {
+ private static final String TAG = "RPCStruct";
+
public static final String KEY_BULK_DATA = "bulkData";
public static final String KEY_PROTECTED = "protected";
@@ -268,7 +270,7 @@ public class RPCStruct implements Cloneable {
return customObject;
} catch (Exception e) {
- e.printStackTrace();
+ DebugTool.logError(TAG,"Error attempting to format an object from a Hashtable", e);
}
} else if (obj instanceof List<?>) {
List<?> list = (List<?>) obj;
@@ -300,7 +302,7 @@ public class RPCStruct implements Cloneable {
}
newList.add(customObject);
} catch (Exception e) {
- e.printStackTrace();
+ DebugTool.logError(TAG,"Error attempting to format object from list of Hashtables", e);
return null;
}
}
@@ -334,15 +336,15 @@ public class RPCStruct implements Cloneable {
try {
valueForString = tClass.getDeclaredMethod("valueForString", String.class);
} catch (NoSuchMethodException e) {
- e.printStackTrace();
+ DebugTool.logError(TAG,"Error attempting to find valueForString method in class", e);
}
if (valueForString != null) {
try {
return valueForString.invoke(null, (String) s);
} catch (IllegalAccessException e) {
- e.printStackTrace();
+ DebugTool.logError(TAG,"Illegal access while using reflection to get enum from string", e);
} catch (InvocationTargetException e) {
- e.printStackTrace();
+ DebugTool.logError(TAG,"Error attempting to use method from reflection to get enum from string", e);
}
}
return null;
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/OnSystemRequest.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/OnSystemRequest.java
index c07c4e2e0..ebe24268a 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/OnSystemRequest.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/OnSystemRequest.java
@@ -179,11 +179,9 @@ public class OnSystemRequest extends RPCNotification {
tempBody = getBody(httpJson);
tempHeaders = getHeaders(httpJson);
} catch (JSONException e) {
- DebugTool.logError(TAG, "HTTPRequest in bulk data was malformed.");
- e.printStackTrace();
+ DebugTool.logError(TAG, "HTTPRequest in bulk data was malformed.", e);
} catch (NullPointerException e) {
- DebugTool.logError(TAG, "Invalid HTTPRequest object in bulk data.");
- e.printStackTrace();
+ DebugTool.logError(TAG, "Invalid HTTPRequest object in bulk data.", e);
}
} else if (RequestType.HTTP.equals(this.getRequestType())) {
tempHeaders = new Headers();
@@ -209,8 +207,7 @@ public class OnSystemRequest extends RPCNotification {
try {
result = httpJson.getString(KEY_BODY);
} catch (JSONException e) {
- DebugTool.logError(TAG, KEY_BODY + " key doesn't exist in bulk data.");
- e.printStackTrace();
+ DebugTool.logError(TAG, KEY_BODY + " key doesn't exist in bulk data.", e);
}
return result;
@@ -224,8 +221,7 @@ public class OnSystemRequest extends RPCNotification {
Hashtable<String, Object> httpHeadersHash = JsonRPCMarshaller.deserializeJSONObject(httpHeadersJson);
result = new Headers(httpHeadersHash);
} catch (JSONException e) {
- DebugTool.logError(TAG, KEY_HEADERS + " key doesn't exist in bulk data.");
- e.printStackTrace();
+ DebugTool.logError(TAG, KEY_HEADERS + " key doesn't exist in bulk data.", e);
}
return result;
diff --git a/base/src/main/java/com/smartdevicelink/streaming/video/RTPH264Packetizer.java b/base/src/main/java/com/smartdevicelink/streaming/video/RTPH264Packetizer.java
index ce8d900ca..4e46b3632 100644
--- a/base/src/main/java/com/smartdevicelink/streaming/video/RTPH264Packetizer.java
+++ b/base/src/main/java/com/smartdevicelink/streaming/video/RTPH264Packetizer.java
@@ -124,7 +124,7 @@ public class RTPH264Packetizer extends AbstractPacketizer implements IVideoStrea
bufferSize = MAX_DATA_SIZE_FOR_ENCRYPTED_SERVICE;
}
- mOutputQueue = new LinkedBlockingQueue<>(MAX_QUEUE_SIZE / bufferSize);
+ mOutputQueue = new LinkedBlockingQueue<>(Math.max(MAX_QUEUE_SIZE / bufferSize, 1));
mNALUnitReader = new NALUnitReader();
mPayloadType = DEFAULT_RTP_PAYLOAD_TYPE;
diff --git a/base/src/main/java/com/smartdevicelink/transport/SdlPsm.java b/base/src/main/java/com/smartdevicelink/transport/SdlPsm.java
index 99510b061..dc8fd3bbc 100644
--- a/base/src/main/java/com/smartdevicelink/transport/SdlPsm.java
+++ b/base/src/main/java/com/smartdevicelink/transport/SdlPsm.java
@@ -32,15 +32,18 @@
package com.smartdevicelink.transport;
import com.smartdevicelink.protocol.SdlPacket;
+import com.smartdevicelink.util.DebugTool;
import static com.smartdevicelink.protocol.SdlProtocol.V1_HEADER_SIZE;
import static com.smartdevicelink.protocol.SdlProtocol.V1_V2_MTU_SIZE;
+import static com.smartdevicelink.protocol.SdlProtocol.V2_HEADER_SIZE;
+import static com.smartdevicelink.protocol.SdlProtocol.V3_V4_MTU_SIZE;
public class SdlPsm {
- //private static final String TAG = "Sdl PSM";
- //Each state represents the byte that should be incoming
+ private static final String TAG = "Sdl PSM";
+ //Each state represents the byte that should be incoming
public static final int START_STATE = 0x0;
public static final int SERVICE_TYPE_STATE = 0x02;
public static final int CONTROL_FRAME_INFO_STATE = 0x03;
@@ -83,8 +86,13 @@ public class SdlPsm {
}
public boolean handleByte(byte data) {
- //Log.trace(TAG, data + " = incoming");
- state = transitionOnInput(data, state);
+ try {
+ state = transitionOnInput(data, state);
+ } catch (Exception e) {
+ DebugTool.logError(TAG, "Exception thrown while parsing byte - " + data, e);
+ state = ERROR_STATE;
+ return false;
+ }
return state != ERROR_STATE;
}
@@ -93,18 +101,11 @@ public class SdlPsm {
switch (state) {
case START_STATE:
version = (rawByte & (byte) VERSION_MASK) >> 4;
- //Log.trace(TAG, "Version: " + version);
- if (version == 0) { //It should never be 0
- return ERROR_STATE;
- }
encrypted = (1 == ((rawByte & (byte) ENCRYPTION_MASK) >> 3));
-
-
frameType = rawByte & (byte) FRAME_TYPE_MASK;
- //Log.trace(TAG, rawByte + " = Frame Type: " + frameType);
- if ((version < 1 || version > 5) //These are known versions supported by this library.
- && frameType != SdlPacket.FRAME_TYPE_CONTROL) {
+ if ((version < 1 || version > 5)) {
+ //These are known versions supported by this library.
return ERROR_STATE;
}
@@ -116,7 +117,16 @@ public class SdlPsm {
case SERVICE_TYPE_STATE:
serviceType = (int) (rawByte & 0xFF);
- return CONTROL_FRAME_INFO_STATE;
+ switch (serviceType) {
+ case 0x00: //SessionType.CONTROL:
+ case 0x07: //SessionType.RPC:
+ case 0x0A: //SessionType.PCM (Audio):
+ case 0x0B: //SessionType.NAV (Video):
+ case 0x0F: //SessionType.BULK (Hybrid):
+ return CONTROL_FRAME_INFO_STATE;
+ default:
+ return ERROR_STATE;
+ }
case CONTROL_FRAME_INFO_STATE:
controlFrameInfo = (int) (rawByte & 0xFF);
@@ -203,19 +213,34 @@ public class SdlPsm {
default:
return ERROR_STATE;
}
- if (version == 1) { //Version 1 packets will not have message id's
- if (dataLength == 0) {
- return FINISHED_STATE; //We are done if we don't have any payload
- }
- if (dataLength <= V1_V2_MTU_SIZE - V1_HEADER_SIZE) { // sizes from protocol/WiProProtocol.java
- payload = new byte[dataLength];
- } else {
- return ERROR_STATE;
- }
- dumpSize = dataLength;
- return DATA_PUMP_STATE;
- } else {
- return MESSAGE_1_STATE;
+ switch (version) {
+ case 1:
+ //Version 1 packets will not have message id's
+ if (dataLength == 0) {
+ return FINISHED_STATE; //We are done if we don't have any payload
+ }
+ if (dataLength <= V1_V2_MTU_SIZE - V1_HEADER_SIZE) { // sizes from SDL protocol
+ payload = new byte[dataLength];
+ } else {
+ return ERROR_STATE;
+ }
+ dumpSize = dataLength;
+ return DATA_PUMP_STATE;
+ case 2:
+ if (dataLength <= V1_V2_MTU_SIZE - V2_HEADER_SIZE) {
+ return MESSAGE_1_STATE;
+ } else {
+ return ERROR_STATE;
+ }
+ case 3:
+ case 4:
+ if (dataLength <= V3_V4_MTU_SIZE - V2_HEADER_SIZE) {
+ return MESSAGE_1_STATE;
+ } else {
+ return ERROR_STATE;
+ }
+ default:
+ return MESSAGE_1_STATE;
}
case MESSAGE_1_STATE:
diff --git a/base/src/main/java/com/smartdevicelink/transport/SiphonServer.java b/base/src/main/java/com/smartdevicelink/transport/SiphonServer.java
index cd22abc04..39871b777 100644
--- a/base/src/main/java/com/smartdevicelink/transport/SiphonServer.java
+++ b/base/src/main/java/com/smartdevicelink/transport/SiphonServer.java
@@ -107,7 +107,7 @@ public class SiphonServer {
try {
SiphonServer.closeServer();
} catch (IOException e) {
- e.printStackTrace();
+ DebugTool.logError(TAG,"Error while trying to close siphon server", e);
}
return m_listenPort;
diff --git a/base/src/main/java/com/smartdevicelink/util/FileUtls.java b/base/src/main/java/com/smartdevicelink/util/FileUtls.java
index 449fc6ea9..747052415 100644
--- a/base/src/main/java/com/smartdevicelink/util/FileUtls.java
+++ b/base/src/main/java/com/smartdevicelink/util/FileUtls.java
@@ -74,7 +74,7 @@ public class FileUtls {
try {
return Files.readAllBytes(file.toPath());
} catch (IOException e) {
- e.printStackTrace();
+ DebugTool.logError(TAG,"Error trying to get file data", e);
}
}
}
diff --git a/javaEE/javaEE/gradle.properties b/javaEE/javaEE/gradle.properties
index deba1f427..9dbf6adf2 100644
--- a/javaEE/javaEE/gradle.properties
+++ b/javaEE/javaEE/gradle.properties
@@ -1,6 +1,6 @@
GROUP=com.smartdevicelink
POM_ARTIFACT_ID=sdl_java_ee
-VERSION_NAME=5.1.1
+VERSION_NAME=5.2.0
POM_NAME=sdl_java_ee
POM_PACKAGING=jar
diff --git a/javaSE/javaSE/gradle.properties b/javaSE/javaSE/gradle.properties
index 7b3651b0c..b8f580351 100644
--- a/javaSE/javaSE/gradle.properties
+++ b/javaSE/javaSE/gradle.properties
@@ -1,6 +1,6 @@
GROUP=com.smartdevicelink
POM_ARTIFACT_ID=sdl_java_se
-VERSION_NAME=5.1.1
+VERSION_NAME=5.2.0
POM_NAME=sdl_java_se
POM_PACKAGING=jar
diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java
index 1e3915205..30aaeaed4 100644
--- a/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java
+++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java
@@ -32,5 +32,5 @@ package com.smartdevicelink;
// THIS FILE IS AUTO GENERATED, DO NOT MODIFY!!
public final class BuildConfig {
- public static final String VERSION_NAME = "5.1.1";
+ public static final String VERSION_NAME = "5.2.0";
} \ No newline at end of file