summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java27
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java178
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperationTests.java2
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java193
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java443
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperationTests.java295
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java73
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/SecurityQueryPayloadTests.java126
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryErrorCodeTests.java189
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryIDTests.java91
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryTypeTests.java97
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/BaseChoiceSetManager.java316
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCell.java35
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java74
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java286
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java923
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java374
-rw-r--r--base/src/main/java/com/smartdevicelink/protocol/BaseSdlPacket.java5
-rw-r--r--base/src/main/java/com/smartdevicelink/protocol/ProtocolMessage.java5
-rw-r--r--base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java6
-rw-r--r--base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java157
-rw-r--r--base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryErrorCode.java61
-rw-r--r--base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryID.java105
-rw-r--r--base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryType.java41
-rw-r--r--base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java65
25 files changed, 2581 insertions, 1586 deletions
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java
index 0dde875ea..a761b1f88 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java
@@ -61,7 +61,7 @@ public class ChoiceCellTests {
choiceCell.setVoiceCommands(TestValues.GENERAL_STRING_LIST);
choiceCell.setArtwork(artwork);
choiceCell.setSecondaryArtwork(artwork);
- choiceCell.setUniqueText(TestValues.GENERAL_STRING);
+ choiceCell.setUniqueTextId(TestValues.GENERAL_INT);
// use getters and assert equality
assertEquals(choiceCell.getText(), TestValues.GENERAL_STRING);
@@ -71,7 +71,7 @@ public class ChoiceCellTests {
assertEquals(choiceCell.getArtwork(), artwork);
assertEquals(choiceCell.getSecondaryArtwork(), artwork);
assertEquals(choiceCell.getChoiceId(), MAX_ID);
- assertEquals(choiceCell.getUniqueText(), TestValues.GENERAL_STRING);
+ assertEquals(choiceCell.getUniqueTextId(), TestValues.GENERAL_INTEGER);
}
@Test
@@ -121,14 +121,27 @@ public class ChoiceCellTests {
choiceCell3.setSecondaryText(TestValues.GENERAL_STRING);
choiceCell3.setTertiaryText(TestValues.GENERAL_STRING);
- //UniqueText should not be taken into consideration when checking equality
- choiceCell.setUniqueText(TestValues.GENERAL_STRING);
- choiceCell2.setUniqueText(TestValues.GENERAL_STRING);
- choiceCell3.setUniqueText(TestValues.GENERAL_STRING);
-
// Make sure our overridden method works, even though these are different objects in memory
assertTrue(choiceCell.equals(choiceCell2));
assertFalse(choiceCell.equals(choiceCell3));
}
+
+ @Test
+ public void testGetUniqueCellText() {
+ ChoiceCell choiceCell = new ChoiceCell("Test");
+ ChoiceCell choiceCell2 = new ChoiceCell("Test");
+ choiceCell2.setUniqueTextId(2);
+ ChoiceCell choiceCell3 = new ChoiceCell("Test");
+ choiceCell3.setUniqueTextId(3);
+
+ assertEquals((int) choiceCell.getUniqueTextId(), 1);
+ assertEquals(choiceCell.getUniqueText(), "Test");
+
+ assertEquals((int) choiceCell2.getUniqueTextId(), 2);
+ assertEquals(choiceCell2.getUniqueText(), "Test (2)");
+
+ assertEquals((int) choiceCell3.getUniqueTextId(), 3);
+ assertEquals(choiceCell3.getUniqueText(), "Test (3)");
+ }
}
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 81f912361..f26a7a5c5 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
@@ -38,25 +38,21 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.BaseSubManager;
+import com.smartdevicelink.managers.CompletionListener;
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;
@@ -68,13 +64,10 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNotNull;
-import static junit.framework.TestCase.assertNotSame;
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
import static org.mockito.Mockito.doReturn;
@@ -103,16 +96,12 @@ public class ChoiceSetManagerTests {
assertEquals(csm.getState(), BaseSubManager.SETTING_UP);
assertEquals(csm.currentSystemContext, SystemContext.SYSCTXT_MAIN);
assertEquals(csm.currentHMILevel, HMILevel.HMI_NONE);
- assertEquals(csm.choiceCellIdMin, 1);
- assertEquals(csm.nextChoiceId, 1);
assertFalse(csm.isVROptional);
assertNotNull(csm.fileManager);
assertNotNull(csm.preloadedChoices);
- assertNotNull(csm.pendingPreloadChoices);
assertNotNull(csm.transactionQueue);
assertNotNull(csm.hmiListener);
assertNotNull(csm.onDisplayCapabilityListener);
- assertNull(csm.pendingPresentOperation);
}
@After
@@ -123,11 +112,8 @@ public class ChoiceSetManagerTests {
assertNull(csm.currentHMILevel);
assertNull(csm.currentSystemContext);
assertNull(csm.defaultMainWindowCapability);
- assertNull(csm.pendingPresentationSet);
- assertNull(csm.pendingPresentOperation);
assertEquals(csm.transactionQueue.getTasksAsList().size(), 0);
- assertEquals(csm.nextChoiceId, 1);
assertFalse(csm.isVROptional);
@@ -160,12 +146,6 @@ public class ChoiceSetManagerTests {
ChoiceSet choiceSet1 = new ChoiceSet("test", Collections.<ChoiceCell>emptyList(), choiceSetSelectionListener);
assertFalse(csm.setUpChoiceSet(choiceSet1));
- // Identical cells will not be allowed
- ChoiceCell cell1 = new ChoiceCell("test");
- ChoiceCell cell2 = new ChoiceCell("test");
- ChoiceSet choiceSet2 = new ChoiceSet("test", Arrays.asList(cell1, cell2), choiceSetSelectionListener);
- assertFalse(csm.setUpChoiceSet(choiceSet2));
-
// cells that have duplicate text will be allowed if there is another property to make them unique because a unique name will be assigned and used
ChoiceCell cell3 = new ChoiceCell("test");
cell3.setSecondaryText("text 1");
@@ -208,103 +188,56 @@ public class ChoiceSetManagerTests {
}
@Test
- public void testUpdateIdsOnChoices() {
-
+ public void preloadChoicesAddsToQueue() {
ChoiceCell cell1 = new ChoiceCell("test");
ChoiceCell cell2 = new ChoiceCell("test2");
ChoiceCell cell3 = new ChoiceCell("test3");
- LinkedHashSet<ChoiceCell> cellSet = new LinkedHashSet<>();
+ ArrayList<ChoiceCell> cellSet = new ArrayList<>();
cellSet.add(cell1);
cellSet.add(cell2);
cellSet.add(cell3);
- // Cells are initially set to MAX_ID
- assertEquals(cell1.getChoiceId(), 2000000000);
- assertEquals(cell2.getChoiceId(), 2000000000);
- assertEquals(cell3.getChoiceId(), 2000000000);
- csm.updateIdsOnChoices(cellSet);
- // We are looking for unique IDs
- assertNotSame(cell1.getChoiceId(), 2000000000);
- assertNotSame(cell2.getChoiceId(), 2000000000);
- assertNotSame(cell3.getChoiceId(), 2000000000);
- }
+ csm.preloadChoices(cellSet, new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
- @Test
- public void testAddUniqueNamesToCells() {
- ChoiceCell cell1 = new ChoiceCell("McDonalds", "1 mile away", null, null, null, null);
- ChoiceCell cell2 = new ChoiceCell("McDonalds", "2 mile away", null, null, null, null);
- ChoiceCell cell3 = new ChoiceCell("Starbucks", "3 mile away", null, null, null, null);
- 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);
- List<ChoiceCell> cellList = new ArrayList<>();
-
- cellList.add(cell1);
- cellList.add(cell2);
- cellList.add(cell3);
- cellList.add(cell4);
- cellList.add(cell5);
- cellList.add(cell6);
-
- csm.addUniqueNamesToCells(cellList);
-
- assertEquals(cell1.getUniqueText(), "McDonalds");
- assertEquals(cell2.getUniqueText(), "McDonalds (2)");
- assertEquals(cell3.getUniqueText(), "Starbucks");
- assertEquals(cell4.getUniqueText(), "McDonalds (3)");
- assertEquals(cell5.getUniqueText(), "Starbucks (2)");
- assertEquals(cell6.getUniqueText(), "Meijer");
+ }
+ });
+ assertEquals(csm.transactionQueue.getTasksAsList().size(), 1);
}
@Test
- public void testChoicesToBeRemovedFromPendingWithArray() {
-
- ChoiceCell cell1 = new ChoiceCell("test");
- ChoiceCell cell2 = new ChoiceCell("test2");
- ChoiceCell cell3 = new ChoiceCell("test3");
-
- HashSet<ChoiceCell> pendingPreloadSet = new HashSet<>();
- pendingPreloadSet.add(cell1);
- pendingPreloadSet.add(cell2);
- pendingPreloadSet.add(cell3);
-
- csm.pendingPreloadChoices.clear();
- csm.pendingPreloadChoices = pendingPreloadSet;
-
- List<ChoiceCell> choices = new ArrayList<>();
- choices.add(cell2);
-
- HashSet<ChoiceCell> returnedChoices = csm.choicesToBeRemovedFromPendingWithArray(choices);
+ public void preloadChoicesQueueEmptyWhenNoChoiceCells() {
+ ArrayList<ChoiceCell> cellSet = new ArrayList<>();
+ csm.preloadChoices(cellSet, new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
- assertEquals(returnedChoices.size(), 1);
- for (ChoiceCell cell : returnedChoices) {
- assertEquals(cell.getText(), "test2");
- }
+ }
+ });
+ assertEquals(csm.transactionQueue.getTasksAsList().size(), 0);
}
@Test
- public void testChoicesToBeUploadedWithArray() {
-
+ public void testPreloadChoicesQueueEmptyIfFileManagerNull() {
ChoiceCell cell1 = new ChoiceCell("test");
ChoiceCell cell2 = new ChoiceCell("test2");
ChoiceCell cell3 = new ChoiceCell("test3");
+ ArrayList<ChoiceCell> cellSet = new ArrayList<>();
+ cellSet.add(cell1);
+ cellSet.add(cell2);
+ cellSet.add(cell3);
- HashSet<ChoiceCell> pendingDeleteSet = new HashSet<>();
- pendingDeleteSet.add(cell1);
- pendingDeleteSet.add(cell2);
- pendingDeleteSet.add(cell3);
-
- csm.preloadedChoices.clear();
- csm.preloadedChoices = pendingDeleteSet;
-
- List<ChoiceCell> choices = new ArrayList<>();
- choices.add(cell2);
-
- HashSet<ChoiceCell> returnedChoices = csm.choicesToBeDeletedWithArray(choices);
+ ISdl internalInterface = mock(ISdl.class);
+ when(internalInterface.getTaskmaster()).thenReturn(taskmaster);
+ FileManager fileManager = null;
+ ChoiceSetManager newCSM = new ChoiceSetManager(internalInterface, fileManager);
+ newCSM.preloadChoices(cellSet, new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
- assertEquals(returnedChoices.size(), 1);
- for (ChoiceCell cell : returnedChoices) {
- assertEquals(cell.getText(), "test2");
- }
+ }
+ });
+ assertEquals(csm.transactionQueue.getTasksAsList().size(), 0);
}
@Test
@@ -474,51 +407,4 @@ 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/choiceset/DeleteChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperationTests.java
index 2b472eda8..fdb141b07 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperationTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperationTests.java
@@ -66,7 +66,7 @@ public class DeleteChoicesOperationTests {
cellsToDelete.add(cell2);
ISdl internalInterface = mock(ISdl.class);
- deleteChoicesOperation = new DeleteChoicesOperation(internalInterface, cellsToDelete, null);
+ deleteChoicesOperation = new DeleteChoicesOperation(internalInterface, cellsToDelete, null, null);
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java
deleted file mode 100644
index 9e879a73a..000000000
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (c) 2019 Livio, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * Neither the name of the Livio Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * Created by brettywhite on 6/12/19 1:52 PM
- *
- */
-
-package com.smartdevicelink.managers.screen.choiceset;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.smartdevicelink.managers.ISdl;
-import com.smartdevicelink.managers.file.FileManager;
-import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
-import com.smartdevicelink.managers.file.filetypes.SdlFile;
-import com.smartdevicelink.proxy.rpc.ImageField;
-import com.smartdevicelink.proxy.rpc.TextField;
-import com.smartdevicelink.proxy.rpc.WindowCapability;
-import com.smartdevicelink.proxy.rpc.enums.CharacterSet;
-import com.smartdevicelink.proxy.rpc.enums.FileType;
-import com.smartdevicelink.proxy.rpc.enums.ImageFieldName;
-import com.smartdevicelink.proxy.rpc.enums.ImageType;
-import com.smartdevicelink.proxy.rpc.enums.TextFieldName;
-import com.smartdevicelink.test.TestValues;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-
-import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertFalse;
-import static junit.framework.TestCase.assertNotNull;
-import static junit.framework.TestCase.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-@RunWith(AndroidJUnit4.class)
-public class PreloadChoicesOperationTests {
-
- private PreloadChoicesOperation preloadChoicesOperation;
- private PreloadChoicesOperation preloadChoicesOperationNullCapability;
- private PreloadChoicesOperation preloadChoicesOperationEmptyCapability;
-
-
- @Before
- public void setUp() throws Exception {
-
- ChoiceCell cell1 = new ChoiceCell("cell 1");
- ChoiceCell cell2 = new ChoiceCell("cell 2", null, TestValues.GENERAL_ARTWORK);
- LinkedHashSet<ChoiceCell> cellsToPreload = new LinkedHashSet<>();
- cellsToPreload.add(cell1);
- cellsToPreload.add(cell2);
-
- ImageField imageField = new ImageField(ImageFieldName.choiceImage, Arrays.asList(FileType.GRAPHIC_PNG, FileType.GRAPHIC_JPEG));
- ImageField imageField2 = new ImageField();
- imageField2.setName(ImageFieldName.choiceSecondaryImage);
- TextField textField = new TextField(TextFieldName.menuName, CharacterSet.CID1SET, 2, 2);
-
- TextField textField2 = new TextField();
- TextField textField3 = new TextField();
-
- textField2.setName(TextFieldName.secondaryText);
- textField3.setName(TextFieldName.tertiaryText);
-
-
- WindowCapability windowCapability = new WindowCapability();
- windowCapability.setImageFields(Arrays.asList(imageField, imageField2));
- windowCapability.setImageTypeSupported(Arrays.asList(ImageType.STATIC, ImageType.DYNAMIC));
- windowCapability.setTextFields(Arrays.asList(textField, textField2, textField3));
-
- ISdl internalInterface = mock(ISdl.class);
- FileManager fileManager = mock(FileManager.class);
-
- // We still want the mock fileManager to use the real implementation for fileNeedsUpload()
- when(fileManager.fileNeedsUpload(any(SdlFile.class))).thenCallRealMethod();
-
- preloadChoicesOperation = new PreloadChoicesOperation(internalInterface, fileManager, null, windowCapability, true, cellsToPreload, null);
- }
-
- /**
- * Sets up PreloadChoicesOperation with WindowCapability being null
- */
- public void setUpNullWindowCapability() {
-
- ChoiceCell cell1 = new ChoiceCell("cell 1");
- ChoiceCell cell2 = new ChoiceCell("cell 2", null, TestValues.GENERAL_ARTWORK);
- LinkedHashSet<ChoiceCell> cellsToPreload = new LinkedHashSet<>();
- cellsToPreload.add(cell1);
- cellsToPreload.add(cell2);
-
- ISdl internalInterface = mock(ISdl.class);
- FileManager fileManager = mock(FileManager.class);
- preloadChoicesOperationNullCapability = new PreloadChoicesOperation(internalInterface, fileManager, null, null, true, cellsToPreload, null);
- }
-
- /**
- * Sets up PreloadChoicesOperation with an Capability not being set
- * certain imageFields and TextFields
- */
- public void setUpEmptyWindowCapability() {
-
- ChoiceCell cell1 = new ChoiceCell("cell 1");
- ChoiceCell cell2 = new ChoiceCell("cell 2", null, TestValues.GENERAL_ARTWORK);
- LinkedHashSet<ChoiceCell> cellsToPreload = new LinkedHashSet<>();
- cellsToPreload.add(cell1);
- cellsToPreload.add(cell2);
-
- ImageField imageField = new ImageField();
- imageField.setName(ImageFieldName.alertIcon);
-
- TextField textField = new TextField();
- textField.setName(TextFieldName.mainField1);
-
- WindowCapability windowCapability = new WindowCapability();
- windowCapability.setImageFields(Collections.singletonList(imageField));
- windowCapability.setTextFields(Collections.singletonList(textField));
-
- ISdl internalInterface = mock(ISdl.class);
- FileManager fileManager = mock(FileManager.class);
- preloadChoicesOperationEmptyCapability = new PreloadChoicesOperation(internalInterface, fileManager, null, windowCapability, true, cellsToPreload, null);
- }
-
- @Test
- public void testArtworksToUpload() {
- List<SdlArtwork> artworksToUpload = preloadChoicesOperation.artworksToUpload();
- assertNotNull(artworksToUpload);
- assertEquals(artworksToUpload.size(), 1);
- }
-
- /**
- * Testing shouldSend method's with varying WindowCapability set.
- */
- @Test
- public void testShouldSendText() {
-
- setUpNullWindowCapability();
- assertTrue(preloadChoicesOperationNullCapability.shouldSendChoicePrimaryImage());
- assertTrue(preloadChoicesOperationNullCapability.shouldSendChoiceSecondaryImage());
- assertTrue(preloadChoicesOperationNullCapability.shouldSendChoiceSecondaryText());
- assertTrue(preloadChoicesOperationNullCapability.shouldSendChoiceTertiaryText());
- assertTrue(preloadChoicesOperationNullCapability.shouldSendChoiceText());
-
-
- assertTrue(preloadChoicesOperation.shouldSendChoicePrimaryImage());
- assertTrue(preloadChoicesOperation.shouldSendChoiceSecondaryImage());
- assertTrue(preloadChoicesOperation.shouldSendChoiceSecondaryText());
- assertTrue(preloadChoicesOperation.shouldSendChoiceTertiaryText());
- assertTrue(preloadChoicesOperation.shouldSendChoiceText());
-
- setUpEmptyWindowCapability();
- assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoicePrimaryImage());
- assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoiceSecondaryImage());
- assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoiceSecondaryText());
- assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoiceTertiaryText());
- assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoiceText());
- }
-
-}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java
new file mode 100644
index 000000000..0f3725f53
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java
@@ -0,0 +1,443 @@
+package com.smartdevicelink.managers.screen.choiceset;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.livio.taskmaster.Queue;
+import com.livio.taskmaster.Task;
+import com.livio.taskmaster.Taskmaster;
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.managers.file.FileManager;
+import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
+import com.smartdevicelink.managers.file.filetypes.SdlFile;
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.proxy.RPCResponse;
+import com.smartdevicelink.proxy.rpc.CancelInteraction;
+import com.smartdevicelink.proxy.rpc.ImageField;
+import com.smartdevicelink.proxy.rpc.KeyboardProperties;
+import com.smartdevicelink.proxy.rpc.PerformInteraction;
+import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
+import com.smartdevicelink.proxy.rpc.TextField;
+import com.smartdevicelink.proxy.rpc.WindowCapability;
+import com.smartdevicelink.proxy.rpc.enums.CharacterSet;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+import com.smartdevicelink.proxy.rpc.enums.ImageFieldName;
+import com.smartdevicelink.proxy.rpc.enums.ImageType;
+import com.smartdevicelink.proxy.rpc.enums.InteractionMode;
+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.LayoutMode;
+import com.smartdevicelink.proxy.rpc.enums.TextFieldName;
+import com.smartdevicelink.test.TestValues;
+import com.smartdevicelink.util.Version;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(AndroidJUnit4.class)
+public class PreloadPresentChoicesOperationTests {
+
+ private PreloadPresentChoicesOperation preloadChoicesOperation;
+ private PreloadPresentChoicesOperation preloadChoicesOperationNullCapability;
+ private PreloadPresentChoicesOperation preloadChoicesOperationEmptyCapability;
+
+ private PreloadPresentChoicesOperation presentChoicesOperation;
+ private ChoiceSet choiceSet;
+ private ISdl internalInterface;
+ private FileManager fileManager;
+ private KeyboardListener keyboardListener;
+ private ChoiceSetSelectionListener choiceSetSelectionListener;
+
+ private Taskmaster taskmaster;
+ private Queue queue;
+
+ @Before
+ public void setUp() throws Exception {
+ ChoiceCell cell1 = new ChoiceCell("cell 1");
+ ChoiceCell cell2 = new ChoiceCell("cell 2", null, TestValues.GENERAL_ARTWORK);
+ LinkedHashSet<ChoiceCell> cellsToPreload = new LinkedHashSet<>();
+ cellsToPreload.add(cell1);
+ cellsToPreload.add(cell2);
+
+ ImageField imageField = new ImageField(ImageFieldName.choiceImage, Arrays.asList(FileType.GRAPHIC_PNG, FileType.GRAPHIC_JPEG));
+ ImageField imageField2 = new ImageField();
+ imageField2.setName(ImageFieldName.choiceSecondaryImage);
+ TextField textField = new TextField(TextFieldName.menuName, CharacterSet.CID1SET, 2, 2);
+
+ TextField textField2 = new TextField();
+ TextField textField3 = new TextField();
+
+ textField2.setName(TextFieldName.secondaryText);
+ textField3.setName(TextFieldName.tertiaryText);
+
+
+ WindowCapability windowCapability = new WindowCapability();
+ windowCapability.setImageFields(Arrays.asList(imageField, imageField2));
+ windowCapability.setImageTypeSupported(Arrays.asList(ImageType.STATIC, ImageType.DYNAMIC));
+ windowCapability.setTextFields(Arrays.asList(textField, textField2, textField3));
+
+ internalInterface = mock(ISdl.class);
+ fileManager = mock(FileManager.class);
+
+ // We still want the mock fileManager to use the real implementation for fileNeedsUpload()
+ when(fileManager.fileNeedsUpload(any(SdlFile.class))).thenCallRealMethod();
+
+ preloadChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, null, windowCapability, true, cellsToPreload, null, null);
+
+ keyboardListener = mock(KeyboardListener.class);
+ choiceSetSelectionListener = mock(ChoiceSetSelectionListener.class);
+
+ ChoiceCell cell = new ChoiceCell("Cell1");
+ cell.setChoiceId(0);
+ choiceSet = new ChoiceSet("Test", Collections.singletonList(cell), choiceSetSelectionListener);
+
+ taskmaster = new Taskmaster.Builder().build();
+ queue = taskmaster.createQueue("test", 100, false);
+ taskmaster.start();
+ }
+
+ private KeyboardProperties getKeyBoardProperties() {
+ KeyboardProperties properties = new KeyboardProperties();
+ properties.setLanguage(Language.EN_US);
+ properties.setKeyboardLayout(KeyboardLayout.QWERTZ);
+ properties.setKeypressMode(KeypressMode.RESEND_CURRENT_ENTRY);
+ return properties;
+ }
+
+ /**
+ * Sets up PreloadChoicesOperation with WindowCapability being null
+ */
+ public void setUpNullWindowCapability() {
+
+ ChoiceCell cell1 = new ChoiceCell("cell 1");
+ ChoiceCell cell2 = new ChoiceCell("cell 2", null, TestValues.GENERAL_ARTWORK);
+ LinkedHashSet<ChoiceCell> cellsToPreload = new LinkedHashSet<>();
+ cellsToPreload.add(cell1);
+ cellsToPreload.add(cell2);
+
+ ISdl internalInterface = mock(ISdl.class);
+ preloadChoicesOperationNullCapability = new PreloadPresentChoicesOperation(internalInterface, fileManager, null, null, true, cellsToPreload, null, null);
+ }
+
+ /**
+ * Sets up PreloadChoicesOperation with an Capability not being set
+ * certain imageFields and TextFields
+ */
+ public void setUpEmptyWindowCapability() {
+
+ ChoiceCell cell1 = new ChoiceCell("cell 1");
+ ChoiceCell cell2 = new ChoiceCell("cell 2", null, TestValues.GENERAL_ARTWORK);
+ LinkedHashSet<ChoiceCell> cellsToPreload = new LinkedHashSet<>();
+ cellsToPreload.add(cell1);
+ cellsToPreload.add(cell2);
+
+ ImageField imageField = new ImageField();
+ imageField.setName(ImageFieldName.alertIcon);
+
+ TextField textField = new TextField();
+ textField.setName(TextFieldName.mainField1);
+
+ WindowCapability windowCapability = new WindowCapability();
+ windowCapability.setImageFields(Collections.singletonList(imageField));
+ windowCapability.setTextFields(Collections.singletonList(textField));
+
+ ISdl internalInterface = mock(ISdl.class);
+ preloadChoicesOperationEmptyCapability = new PreloadPresentChoicesOperation(internalInterface, fileManager, null, windowCapability, true, cellsToPreload, null, null);
+ }
+
+ @Test
+ public void testArtworksToUpload() {
+ HashSet<SdlArtwork> artworksToUpload = preloadChoicesOperation.artworksToUpload();
+ assertNotNull(artworksToUpload);
+ assertEquals(artworksToUpload.size(), 1);
+ }
+
+ /**
+ * Testing shouldSend method's with varying WindowCapability set.
+ */
+ @Test
+ public void testShouldSendText() {
+
+ setUpNullWindowCapability();
+ assertTrue(preloadChoicesOperationNullCapability.shouldSendChoicePrimaryImage());
+ assertTrue(preloadChoicesOperationNullCapability.shouldSendChoiceSecondaryImage());
+ assertTrue(preloadChoicesOperationNullCapability.shouldSendChoiceSecondaryText());
+ assertTrue(preloadChoicesOperationNullCapability.shouldSendChoiceTertiaryText());
+ assertTrue(preloadChoicesOperationNullCapability.shouldSendChoiceText());
+
+
+ assertTrue(preloadChoicesOperation.shouldSendChoicePrimaryImage());
+ assertTrue(preloadChoicesOperation.shouldSendChoiceSecondaryImage());
+ assertTrue(preloadChoicesOperation.shouldSendChoiceSecondaryText());
+ assertTrue(preloadChoicesOperation.shouldSendChoiceTertiaryText());
+ assertTrue(preloadChoicesOperation.shouldSendChoiceText());
+
+ setUpEmptyWindowCapability();
+ assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoicePrimaryImage());
+ assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoiceSecondaryImage());
+ assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoiceSecondaryText());
+ assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoiceTertiaryText());
+ assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoiceText());
+ }
+
+
+ @Test
+ public void testGetLayoutMode() {
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(7, 1));
+ // First we will check knowing our keyboard listener is NOT NULL
+ WindowCapability windowCapability = new WindowCapability();
+ HashSet<ChoiceCell> loadedCells = new HashSet<>();
+ presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null, null);
+
+ assertEquals(presentChoicesOperation.getLayoutMode(), LayoutMode.LIST_WITH_SEARCH);
+ presentChoicesOperation.keyboardListener = null;
+ assertEquals(presentChoicesOperation.getLayoutMode(), LayoutMode.LIST_ONLY);
+ }
+
+ @Test
+ public void testGetPerformInteraction() {
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(7, 1));
+ WindowCapability windowCapability = new WindowCapability();
+ HashSet<ChoiceCell> loadedCells = new HashSet<>();
+ presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null, null);
+
+ PerformInteraction pi = presentChoicesOperation.getPerformInteraction();
+ assertEquals(pi.getInitialText(), "Test");
+ assertNull(pi.getHelpPrompt());
+ assertNull(pi.getTimeoutPrompt());
+ assertNull(pi.getVrHelp());
+ assertEquals(pi.getTimeout(), Integer.valueOf(10000));
+ assertEquals(pi.getCancelID(), TestValues.GENERAL_INTEGER);
+ assertEquals(presentChoicesOperation.getLayoutMode(), LayoutMode.LIST_WITH_SEARCH);
+ }
+
+ @Test
+ public void testSetSelectedCellWithId() {
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(7, 1));
+ WindowCapability windowCapability = new WindowCapability();
+ HashSet<ChoiceCell> loadedCells = new HashSet<>();
+ presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null, null);
+
+ assertNull(presentChoicesOperation.selectedCellRow);
+ presentChoicesOperation.setSelectedCellWithId(0);
+ assertEquals(presentChoicesOperation.selectedCellRow, Integer.valueOf(0));
+ }
+
+ private void sleep() {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void testCancelingChoiceSetSuccessfullyIfThreadIsRunning() {
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
+ WindowCapability windowCapability = new WindowCapability();
+ HashSet<ChoiceCell> loadedCells = new HashSet<>();
+ BaseChoiceSetManager.ChoicesOperationCompletionListener listener = new BaseChoiceSetManager.ChoicesOperationCompletionListener() {
+ @Override
+ public void onComplete(boolean success, HashSet<ChoiceCell> loadedChoiceCells) {
+ choiceSet.cancel();
+ sleep();
+
+ verify(internalInterface, times(1)).sendRPC(any(CancelInteraction.class));
+ verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class));
+
+ assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState());
+ }
+ };
+ presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, listener, null);
+ presentChoicesOperation.setLoadedCells(new HashSet<ChoiceCell>());
+ queue.add(presentChoicesOperation, false);
+
+ sleep();
+
+ assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState());
+
+ Answer<Void> cancelInteractionAnswer = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ CancelInteraction cancelInteraction = (CancelInteraction) args[0];
+
+ assertEquals(cancelInteraction.getCancelID(), TestValues.GENERAL_INTEGER);
+ assertEquals(cancelInteraction.getInteractionFunctionID().intValue(), FunctionID.PERFORM_INTERACTION.getId());
+
+ RPCResponse response = new RPCResponse(FunctionID.CANCEL_INTERACTION.toString());
+ response.setSuccess(true);
+ cancelInteraction.getOnRPCResponseListener().onResponse(0, response);
+
+ return null;
+ }
+ };
+ doAnswer(cancelInteractionAnswer).when(internalInterface).sendRPC(any(CancelInteraction.class));
+ }
+
+ @Test
+ public void testCancelingChoiceSetUnsuccessfullyIfThreadIsRunning() {
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
+ WindowCapability windowCapability = new WindowCapability();
+ HashSet<ChoiceCell> loadedCells = new HashSet<>();
+ BaseChoiceSetManager.ChoicesOperationCompletionListener listener = new BaseChoiceSetManager.ChoicesOperationCompletionListener() {
+ @Override
+ public void onComplete(boolean success, HashSet<ChoiceCell> loadedChoiceCells) {
+ choiceSet.cancel();
+ sleep();
+
+ verify(internalInterface, times(1)).sendRPC(any(CancelInteraction.class));
+ verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class));
+
+ assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState());
+ }
+ };
+ presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, listener, null);
+ presentChoicesOperation.setLoadedCells(new HashSet<ChoiceCell>());
+ queue.add(presentChoicesOperation, false);
+ sleep();
+
+ assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState());
+
+ Answer<Void> cancelInteractionAnswer = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ CancelInteraction cancelInteraction = (CancelInteraction) args[0];
+
+ assertEquals(cancelInteraction.getCancelID(), TestValues.GENERAL_INTEGER);
+ assertEquals(cancelInteraction.getInteractionFunctionID().intValue(), FunctionID.PERFORM_INTERACTION.getId());
+
+ RPCResponse response = new RPCResponse(FunctionID.CANCEL_INTERACTION.toString());
+ response.setSuccess(false);
+ cancelInteraction.getOnRPCResponseListener().onResponse(0, response);
+
+ return null;
+ }
+ };
+ doAnswer(cancelInteractionAnswer).when(internalInterface).sendRPC(any(CancelInteraction.class));
+ }
+
+ @Test
+ public void testCancelingChoiceSetIfThreadHasFinished() {
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
+ WindowCapability windowCapability = new WindowCapability();
+ HashSet<ChoiceCell> loadedCells = new HashSet<>();
+ presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER,null, windowCapability, true, loadedCells, null, null);
+ presentChoicesOperation.finishOperation(false);
+
+ assertEquals(Task.FINISHED, presentChoicesOperation.getState());
+
+ choiceSet.cancel();
+ verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
+
+ assertEquals(Task.FINISHED, presentChoicesOperation.getState());
+ }
+
+ @Test
+ public void testCancelingChoiceSetIfThreadHasCanceled() {
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
+ WindowCapability windowCapability = new WindowCapability();
+ HashSet<ChoiceCell> loadedCells = new HashSet<>();
+ presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER,null, windowCapability, true, loadedCells, null, null);
+ presentChoicesOperation.cancelTask();
+
+ assertEquals(Task.CANCELED, presentChoicesOperation.getState());
+
+ choiceSet.cancel();
+ verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
+
+ assertEquals(Task.CANCELED, presentChoicesOperation.getState());
+ }
+
+ @Test
+ public void testCancelingChoiceSetIfThreadHasNotYetRun() {
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
+ WindowCapability windowCapability = new WindowCapability();
+ HashSet<ChoiceCell> loadedCells = new HashSet<>();
+ presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null, null);
+
+ assertEquals(Task.BLOCKED, presentChoicesOperation.getState());
+
+ choiceSet.cancel();
+
+ // Once the operation has started
+ queue.add(presentChoicesOperation, false);
+ sleep();
+
+ assertEquals(Task.CANCELED, presentChoicesOperation.getState());
+
+ // Make sure neither a `CancelInteraction` or `PerformInteraction` RPC is ever sent
+ verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
+ verify(internalInterface, never()).sendRPC(any(PerformInteraction.class));
+ }
+
+ @Test
+ public void testCancelingChoiceSetIfHeadUnitDoesNotSupportFeature() {
+ // Cancel Interaction is only supported on RPC specs v.6.0.0+
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3));
+ WindowCapability windowCapability = new WindowCapability();
+ HashSet<ChoiceCell> loadedCells = new HashSet<>();
+ BaseChoiceSetManager.ChoicesOperationCompletionListener listener = new BaseChoiceSetManager.ChoicesOperationCompletionListener() {
+ @Override
+ public void onComplete(boolean success, HashSet<ChoiceCell> loadedChoiceCells) {
+ choiceSet.cancel();
+ sleep();
+
+ assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState());
+
+ verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
+ verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class));
+ }
+ };
+ presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, listener, null);
+ presentChoicesOperation.setLoadedCells(new HashSet<ChoiceCell>());
+ queue.add(presentChoicesOperation, false);
+ }
+
+ @Test
+ public void testCancelingChoiceSetIfHeadUnitDoesNotSupportFeatureButThreadIsNotRunning() {
+ // Cancel Interaction is only supported on RPC specs v.6.0.0+
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3));
+ WindowCapability windowCapability = new WindowCapability();
+ HashSet<ChoiceCell> loadedCells = new HashSet<>();
+ presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null, null);
+
+ assertEquals(Task.BLOCKED, presentChoicesOperation.getState());
+
+ choiceSet.cancel();
+
+ verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
+
+ // Once the operation has started
+ queue.add(presentChoicesOperation, false);
+ sleep();
+
+ assertEquals(Task.CANCELED, presentChoicesOperation.getState());
+
+ // Make sure neither a `CancelInteraction` or `PerformInteraction` RPC is ever sent
+ verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
+ verify(internalInterface, never()).sendRPC(any(PerformInteraction.class));
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperationTests.java
deleted file mode 100644
index 3d22f352b..000000000
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperationTests.java
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Copyright (c) 2019 Livio, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * Neither the name of the Livio Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * Created by brettywhite on 6/12/19 1:52 PM
- *
- */
-
-package com.smartdevicelink.managers.screen.choiceset;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.livio.taskmaster.Queue;
-import com.livio.taskmaster.Task;
-import com.livio.taskmaster.Taskmaster;
-import com.smartdevicelink.managers.ISdl;
-import com.smartdevicelink.protocol.enums.FunctionID;
-import com.smartdevicelink.proxy.RPCResponse;
-import com.smartdevicelink.proxy.rpc.CancelInteraction;
-import com.smartdevicelink.proxy.rpc.KeyboardProperties;
-import com.smartdevicelink.proxy.rpc.PerformInteraction;
-import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
-import com.smartdevicelink.proxy.rpc.enums.InteractionMode;
-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.LayoutMode;
-import com.smartdevicelink.test.TestValues;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import java.util.Collections;
-
-import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertNull;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-@RunWith(AndroidJUnit4.class)
-public class PresentChoiceSetOperationTests {
-
- private PresentChoiceSetOperation presentChoiceSetOperation;
- private ChoiceSet choiceSet;
- private ISdl internalInterface;
- private KeyboardListener keyboardListener;
- private ChoiceSetSelectionListener choiceSetSelectionListener;
-
- private Taskmaster taskmaster;
- private Queue queue;
-
- @Before
- public void setUp() throws Exception {
-
- internalInterface = mock(ISdl.class);
-
- keyboardListener = mock(KeyboardListener.class);
- choiceSetSelectionListener = mock(ChoiceSetSelectionListener.class);
-
- ChoiceCell cell1 = new ChoiceCell("Cell1");
- cell1.setChoiceId(0);
- choiceSet = new ChoiceSet("Test", Collections.singletonList(cell1), choiceSetSelectionListener);
-
- taskmaster = new Taskmaster.Builder().build();
- queue = taskmaster.createQueue("test", 100, false);
- taskmaster.start();
- }
-
-
- private KeyboardProperties getKeyBoardProperties() {
- KeyboardProperties properties = new KeyboardProperties();
- properties.setLanguage(Language.EN_US);
- properties.setKeyboardLayout(KeyboardLayout.QWERTZ);
- properties.setKeypressMode(KeypressMode.RESEND_CURRENT_ENTRY);
- return properties;
- }
-
- @Test
- public void testGetLayoutMode() {
- // First we will check knowing our keyboard listener is NOT NULL
- presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER);
-
- assertEquals(presentChoiceSetOperation.getLayoutMode(), LayoutMode.LIST_WITH_SEARCH);
- presentChoiceSetOperation.keyboardListener = null;
- assertEquals(presentChoiceSetOperation.getLayoutMode(), LayoutMode.LIST_ONLY);
- }
-
- @Test
- public void testGetPerformInteraction() {
- presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER);
-
- PerformInteraction pi = presentChoiceSetOperation.getPerformInteraction();
- assertEquals(pi.getInitialText(), "Test");
- assertNull(pi.getHelpPrompt());
- assertNull(pi.getTimeoutPrompt());
- assertNull(pi.getVrHelp());
- assertEquals(pi.getTimeout(), Integer.valueOf(10000));
- assertEquals(pi.getCancelID(), TestValues.GENERAL_INTEGER);
- assertEquals(presentChoiceSetOperation.getLayoutMode(), LayoutMode.LIST_WITH_SEARCH);
- }
-
- @Test
- public void testSetSelectedCellWithId() {
- presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER);
-
- assertNull(presentChoiceSetOperation.selectedCellRow);
- presentChoiceSetOperation.setSelectedCellWithId(0);
- assertEquals(presentChoiceSetOperation.selectedCellRow, Integer.valueOf(0));
- }
-
- private void sleep() {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
-
- @Test
- public void testCancelingChoiceSetSuccessfullyIfThreadIsRunning() {
- when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
- presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER);
- queue.add(presentChoiceSetOperation, false);
-
- sleep();
-
- assertEquals(Task.IN_PROGRESS, presentChoiceSetOperation.getState());
-
- choiceSet.cancel();
- Answer<Void> cancelInteractionAnswer = new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- Object[] args = invocation.getArguments();
- CancelInteraction cancelInteraction = (CancelInteraction) args[0];
-
- assertEquals(cancelInteraction.getCancelID(), TestValues.GENERAL_INTEGER);
- assertEquals(cancelInteraction.getInteractionFunctionID().intValue(), FunctionID.PERFORM_INTERACTION.getId());
-
- RPCResponse response = new RPCResponse(FunctionID.CANCEL_INTERACTION.toString());
- response.setSuccess(true);
- cancelInteraction.getOnRPCResponseListener().onResponse(0, response);
-
- return null;
- }
- };
- doAnswer(cancelInteractionAnswer).when(internalInterface).sendRPC(any(CancelInteraction.class));
-
- verify(internalInterface, times(1)).sendRPC(any(CancelInteraction.class));
- verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class));
-
- assertEquals(Task.IN_PROGRESS, presentChoiceSetOperation.getState());
- }
-
- @Test
- public void testCancelingChoiceSetUnsuccessfullyIfThreadIsRunning() {
- when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
- presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER);
- queue.add(presentChoiceSetOperation, false);
- sleep();
-
- assertEquals(Task.IN_PROGRESS, presentChoiceSetOperation.getState());
-
- choiceSet.cancel();
- Answer<Void> cancelInteractionAnswer = new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- Object[] args = invocation.getArguments();
- CancelInteraction cancelInteraction = (CancelInteraction) args[0];
-
- assertEquals(cancelInteraction.getCancelID(), TestValues.GENERAL_INTEGER);
- assertEquals(cancelInteraction.getInteractionFunctionID().intValue(), FunctionID.PERFORM_INTERACTION.getId());
-
- RPCResponse response = new RPCResponse(FunctionID.CANCEL_INTERACTION.toString());
- response.setSuccess(false);
- cancelInteraction.getOnRPCResponseListener().onResponse(0, response);
-
- return null;
- }
- };
- doAnswer(cancelInteractionAnswer).when(internalInterface).sendRPC(any(CancelInteraction.class));
-
- verify(internalInterface, times(1)).sendRPC(any(CancelInteraction.class));
- verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class));
-
- assertEquals(Task.IN_PROGRESS, presentChoiceSetOperation.getState());
- }
-
- @Test
- public void testCancelingChoiceSetIfThreadHasFinished() {
- when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
- presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER);
- presentChoiceSetOperation.finishOperation();
-
- assertEquals(Task.FINISHED, presentChoiceSetOperation.getState());
-
- choiceSet.cancel();
- verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
-
- assertEquals(Task.FINISHED, presentChoiceSetOperation.getState());
- }
-
- @Test
- public void testCancelingChoiceSetIfThreadHasNotYetRun() {
- when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
- presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER);
-
- assertEquals(Task.BLOCKED, presentChoiceSetOperation.getState());
-
- choiceSet.cancel();
-
- // Once the operation has started
- queue.add(presentChoiceSetOperation, false);
- sleep();
-
- assertEquals(Task.CANCELED, presentChoiceSetOperation.getState());
-
- // Make sure neither a `CancelInteraction` or `PerformInteraction` RPC is ever sent
- verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
- verify(internalInterface, never()).sendRPC(any(PerformInteraction.class));
- }
-
- @Test
- public void testCancelingChoiceSetIfHeadUnitDoesNotSupportFeature() {
- // Cancel Interaction is only supported on RPC specs v.6.0.0+
- when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3));
- presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER);
- queue.add(presentChoiceSetOperation, false);
- sleep();
-
- assertEquals(Task.IN_PROGRESS, presentChoiceSetOperation.getState());
-
- choiceSet.cancel();
-
- verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
- verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class));
- }
-
- @Test
- public void testCancelingChoiceSetIfHeadUnitDoesNotSupportFeatureButThreadIsNotRunning() {
- // Cancel Interaction is only supported on RPC specs v.6.0.0+
- when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3));
- presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER);
-
- assertEquals(Task.BLOCKED, presentChoiceSetOperation.getState());
-
- choiceSet.cancel();
-
- verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
-
- // Once the operation has started
- queue.add(presentChoiceSetOperation, false);
- sleep();
-
- assertEquals(Task.CANCELED, presentChoiceSetOperation.getState());
-
- // Make sure neither a `CancelInteraction` or `PerformInteraction` RPC is ever sent
- verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
- verify(internalInterface, never()).sendRPC(any(PerformInteraction.class));
- }
-} \ No newline at end of file
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java
index e8d89c818..094b29794 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java
@@ -3,6 +3,9 @@ package com.smartdevicelink.test;
import com.smartdevicelink.managers.file.filetypes.SdlFile;
import com.smartdevicelink.protocol.enums.FrameDataControlFrameType;
import com.smartdevicelink.protocol.enums.FrameType;
+import com.smartdevicelink.protocol.enums.SecurityQueryErrorCode;
+import com.smartdevicelink.protocol.enums.SecurityQueryID;
+import com.smartdevicelink.protocol.enums.SecurityQueryType;
import com.smartdevicelink.protocol.enums.SessionType;
import com.smartdevicelink.proxy.rpc.*;
import com.smartdevicelink.proxy.rpc.enums.AppServiceType;
@@ -10,7 +13,6 @@ import com.smartdevicelink.proxy.rpc.enums.DefrostZone;
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.KeyboardLayout;
import com.smartdevicelink.proxy.rpc.enums.PRNDL;
import com.smartdevicelink.proxy.rpc.enums.PrerecordedSpeech;
import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
@@ -126,6 +128,75 @@ public class Validator {
return true;
}
+ public static boolean validateQueryTypeArray(SecurityQueryType[] array1, SecurityQueryType[] array2) {
+
+ if (array1 == null) {
+ return (array2 == null);
+ }
+
+ if (array2 == null) {
+ return (array1 == null);
+ }
+
+ if (array1.length != array2.length) {
+ return false;
+ }
+
+ for (int i = 0; i < array1.length; i++) {
+ if (array1[i] != array2[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static boolean validateQueryIDArray(SecurityQueryID[] array1, SecurityQueryID[] array2) {
+
+ if (array1 == null) {
+ return (array2 == null);
+ }
+
+ if (array2 == null) {
+ return (array1 == null);
+ }
+
+ if (array1.length != array2.length) {
+ return false;
+ }
+
+ for (int i = 0; i < array1.length; i++) {
+ if (array1[i] != array2[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static boolean validateQueryErrorCodeArray(SecurityQueryErrorCode[] array1, SecurityQueryErrorCode[] array2) {
+
+ if (array1 == null) {
+ return (array2 == null);
+ }
+
+ if (array2 == null) {
+ return (array1 == null);
+ }
+
+ if (array1.length != array2.length) {
+ return false;
+ }
+
+ for (int i = 0; i < array1.length; i++) {
+ if (array1[i] != array2[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
public static boolean validateFrameDataControlFrameTypeArray(FrameDataControlFrameType[] array1, FrameDataControlFrameType[] array2) {
if (array1 == null) {
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/SecurityQueryPayloadTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/SecurityQueryPayloadTests.java
new file mode 100644
index 000000000..8c0c36041
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/SecurityQueryPayloadTests.java
@@ -0,0 +1,126 @@
+package com.smartdevicelink.test.protocol;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.smartdevicelink.protocol.SecurityQueryPayload;
+import com.smartdevicelink.protocol.enums.SecurityQueryID;
+import com.smartdevicelink.protocol.enums.SecurityQueryType;
+import com.smartdevicelink.util.BitConverter;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+@RunWith(AndroidJUnit4.class)
+public class SecurityQueryPayloadTests {
+
+ public static SecurityQueryPayload createDummyBqh() {
+ SecurityQueryPayload bqh = new SecurityQueryPayload();
+ bqh.setCorrelationID(123);
+ bqh.setQueryID(SecurityQueryID.SEND_HANDSHAKE_DATA);
+ bqh.setQueryType(SecurityQueryType.REQUEST);
+ bqh.setBulkData(null);
+ bqh.setJsonSize(0);
+ return bqh;
+ }
+
+ public SecurityQueryPayload safeParse(byte[] array) {
+ try {
+ return SecurityQueryPayload.parseBinaryQueryHeader(array);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ @Test
+ public void testCorrectParsing() {
+ byte[] array = new byte[12];
+ array[0] = 0;
+ array[1] = 0;
+ array[2] = 0;
+ array[3] = 2;
+ array[4] = 0;
+ array[5] = 0;
+ array[6] = 0;
+ array[7] = 3;
+ array[8] = 0;
+ array[9] = 0;
+ array[10] = 0;
+ array[11] = 0;
+
+ SecurityQueryPayload parsedBqh = SecurityQueryPayload.parseBinaryQueryHeader(array);
+ assertEquals(parsedBqh.getQueryType(), SecurityQueryType.REQUEST);
+ assertEquals(parsedBqh.getQueryID(), SecurityQueryID.SEND_INTERNAL_ERROR);
+ assertEquals(parsedBqh.getCorrelationID(), 3);
+ assertEquals(parsedBqh.getJsonSize(), 0);
+ }
+
+ @Test
+ public void testCorrectHeaderAssembly() {
+ SecurityQueryPayload dummyBqh = new SecurityQueryPayload();
+ dummyBqh.setQueryType(SecurityQueryType.REQUEST);
+ dummyBqh.setQueryID(SecurityQueryID.SEND_HANDSHAKE_DATA);
+ dummyBqh.setCorrelationID(3);
+ dummyBqh.setJsonSize(0);
+
+ byte[] assembledHeader = dummyBqh.assembleHeaderBytes();
+ assertEquals(dummyBqh.getQueryType(), SecurityQueryType.valueOf(assembledHeader[0]));
+ byte[] queryIDFromHeader = new byte[3];
+ System.arraycopy(assembledHeader, 1, queryIDFromHeader, 0, 3);
+ assertEquals(dummyBqh.getQueryID(), SecurityQueryID.valueOf(queryIDFromHeader));
+ assertEquals(dummyBqh.getCorrelationID(), BitConverter.intFromByteArray(assembledHeader, 4));
+ assertEquals(dummyBqh.getJsonSize(), BitConverter.intFromByteArray(assembledHeader, 8));
+ }
+
+ @Test
+ public void testAssemblyAndParse() {
+ SecurityQueryPayload bqh = createDummyBqh();
+
+ byte[] bqhBytes = bqh.assembleHeaderBytes();
+ assertNotNull(bqhBytes);
+
+ SecurityQueryPayload parsedBqh = SecurityQueryPayload.parseBinaryQueryHeader(bqhBytes);
+ assertNotNull(parsedBqh);
+
+ assertEquals(bqh.getCorrelationID(), parsedBqh.getCorrelationID());
+ assertEquals(bqh.getQueryID(), parsedBqh.getQueryID());
+ assertEquals(bqh.getQueryType(), parsedBqh.getQueryType());
+ assertEquals(bqh.getBulkData(), parsedBqh.getBulkData());
+ assertEquals(bqh.getJsonData(), parsedBqh.getJsonData());
+ assertEquals(bqh.getJsonSize(), parsedBqh.getJsonSize());
+ }
+
+ @Test
+ public void testCorruptHeader() {
+ SecurityQueryPayload bqh = createDummyBqh();
+
+ byte[] bqhBytes = bqh.assembleHeaderBytes();
+
+ assertNotNull(safeParse(bqhBytes));
+
+ int size = bqhBytes.length;
+ for (int i = 0; i < size; i++) {
+ bqhBytes[i] = (byte) 0x99;
+ }
+
+ assertNull(safeParse(bqhBytes));
+ SecurityQueryPayload head = SecurityQueryPayload.parseBinaryQueryHeader(bqhBytes);
+ assertNull(head);
+ }
+
+ @Test
+ public void testJsonSetException() {
+ try {
+ SecurityQueryPayload bqh = createDummyBqh();
+ bqh.setJsonData(null);
+ fail("Setting JSON data to null should have thrown an exception");
+ } catch (Exception e) {
+ //Pass
+ }
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryErrorCodeTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryErrorCodeTests.java
new file mode 100644
index 000000000..0b6cd3f61
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryErrorCodeTests.java
@@ -0,0 +1,189 @@
+package com.smartdevicelink.test.protocol.enums;
+
+import com.smartdevicelink.protocol.enums.SecurityQueryErrorCode;
+import com.smartdevicelink.test.Validator;
+
+import junit.framework.TestCase;
+
+import java.util.Vector;
+
+public class SecurityQueryErrorCodeTests extends TestCase {
+
+ private Vector<SecurityQueryErrorCode> list = SecurityQueryErrorCode.getList();
+
+ public void testValidEnums() {
+ final byte ERROR_SUCCESS_BYTE = (byte) 0x00;
+ final String ERROR_SUCCESS_STRING = "ERROR_SUCCESS";
+
+ final byte ERROR_INVALID_QUERY_SIZE_BYTE = (byte) 0x01;
+ final String ERROR_INVALID_QUERY_SIZE_STRING = "ERROR_INVALID_QUERY_SIZE";
+
+ final byte ERROR_INVALID_QUERY_ID_BYTE = (byte) 0x02;
+ final String ERROR_INVALID_QUERY_ID_STRING = "ERROR_INVALID_QUERY_ID";
+
+ final byte ERROR_NOT_SUPPORTED_BYTE = (byte) 0x03;
+ final String ERROR_NOT_SUPPORTED_STRING = "ERROR_NOT_SUPPORTED";
+
+ final byte ERROR_SERVICE_ALREADY_PROTECTED_BYTE = (byte) 0x04;
+ final String ERROR_SERVICE_ALREADY_PROTECTED_STRING = "ERROR_SERVICE_ALREADY_PROTECTED";
+
+ final byte ERROR_SERVICE_NOT_PROTECTED_BYTE = (byte) 0x05;
+ final String ERROR_SERVICE_NOT_PROTECTED_STRING = "ERROR_SERVICE_NOT_PROTECTED";
+
+ final byte ERROR_DECRYPTION_FAILED_BYTE = (byte) 0x06;
+ final String ERROR_DECRYPTION_FAILED_STRING = "ERROR_DECRYPTION_FAILED";
+
+ final byte ERROR_ENCRYPTION_FAILED_BYTE = (byte) 0x07;
+ final String ERROR_ENCRYPTION_FAILED_STRING = "ERROR_ENCRYPTION_FAILED";
+
+ final byte ERROR_SSL_INVALID_DATA_BYTE = (byte) 0x08;
+ final String ERROR_SSL_INVALID_DATA_STRING = "ERROR_SSL_INVALID_DATA";
+
+ final byte ERROR_HANDSHAKE_FAILED_BYTE = (byte) 0x09;
+ final String ERROR_HANDSHAKE_FAILED_STRING = "ERROR_HANDSHAKE_FAILED";
+
+ final byte INVALID_CERT_BYTE = (byte) 0x0A;
+ final String INVALID_CERT_STRING = "INVALID_CERT";
+
+ final byte EXPIRED_CERT_BYTE = (byte) 0x0B;
+ final String EXPIRED_CERT_STRING = "EXPIRED_CERT";
+
+ final byte ERROR_INTERNAL_BYTE = (byte) 0xFF;
+ final String ERROR_INTERNAL_STRING = "ERROR_INTERNAL";
+
+ final byte ERROR_UNKNOWN_INTERNAL_ERROR_BYTE = (byte) 0xFE;
+ final String ERROR_UNKNOWN_INTERNAL_ERROR_STRING = "ERROR_UNKNOWN_INTERNAL_ERROR";
+
+ try {
+ assertNotNull("QueryErrorCode list returned null", list);
+
+ SecurityQueryErrorCode enumSuccess = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_SUCCESS_BYTE);
+ SecurityQueryErrorCode enumInvalidQuerySize = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_INVALID_QUERY_SIZE_BYTE);
+ SecurityQueryErrorCode enumInvalidQueryID = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_INVALID_QUERY_ID_BYTE);
+ SecurityQueryErrorCode enumNotSupported = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_NOT_SUPPORTED_BYTE);
+ SecurityQueryErrorCode enumServiceAlreadyProtected = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_SERVICE_ALREADY_PROTECTED_BYTE);
+ SecurityQueryErrorCode enumServiceNotProtected = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_SERVICE_NOT_PROTECTED_BYTE);
+ SecurityQueryErrorCode enumDecryptionFailed = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_DECRYPTION_FAILED_BYTE);
+ SecurityQueryErrorCode enumEncryptionFailed = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_ENCRYPTION_FAILED_BYTE);
+ SecurityQueryErrorCode enumSSLInvalidData = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_SSL_INVALID_DATA_BYTE);
+ SecurityQueryErrorCode enumHandshakeFailed = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_HANDSHAKE_FAILED_BYTE);
+ SecurityQueryErrorCode enumInvalidCert = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, INVALID_CERT_BYTE);
+ SecurityQueryErrorCode enumExpiredCert = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, EXPIRED_CERT_BYTE);
+ SecurityQueryErrorCode enumInternal = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_INTERNAL_BYTE);
+ SecurityQueryErrorCode enumUnknownInternalError = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_UNKNOWN_INTERNAL_ERROR_BYTE);
+
+ assertNotNull("Success byte match returned null", enumSuccess);
+ assertNotNull("Invalid Query Size byte match returned null", enumInvalidQuerySize);
+ assertNotNull("Invalid Query ID byte match returned null", enumInvalidQueryID);
+ assertNotNull("Not Supported byte match returned null", enumNotSupported);
+ assertNotNull("Service Already Protected byte match returned null", enumServiceAlreadyProtected);
+ assertNotNull("Service Not Protected byte match returned null", enumServiceNotProtected);
+ assertNotNull("Decryption Failed byte match returned null", enumDecryptionFailed);
+ assertNotNull("Encryption Failed byte match returned null", enumEncryptionFailed);
+ assertNotNull("SSL Invalid Data byte match returned null", enumSSLInvalidData);
+ assertNotNull("Handshake Failed byte match returned null", enumHandshakeFailed);
+ assertNotNull("Invalid Cert byte match returned null", enumInvalidCert);
+ assertNotNull("Expired Cert byte match returned null", enumExpiredCert);
+ assertNotNull("Internal byte match returned null", enumInternal);
+ assertNotNull("Unknown Internal byte match returned null", enumUnknownInternalError);
+
+ enumSuccess = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_SUCCESS_STRING);
+ enumInvalidQuerySize = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_INVALID_QUERY_SIZE_STRING);
+ enumInvalidQueryID = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_INVALID_QUERY_ID_STRING);
+ enumNotSupported = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_NOT_SUPPORTED_STRING);
+ enumServiceAlreadyProtected = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_SERVICE_ALREADY_PROTECTED_STRING);
+ enumServiceNotProtected = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_SERVICE_NOT_PROTECTED_STRING);
+ enumDecryptionFailed = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_DECRYPTION_FAILED_STRING);
+ enumEncryptionFailed = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_ENCRYPTION_FAILED_STRING);
+ enumSSLInvalidData = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_SSL_INVALID_DATA_STRING);
+ enumHandshakeFailed = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_HANDSHAKE_FAILED_STRING);
+ enumInvalidCert = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, INVALID_CERT_STRING);
+ enumExpiredCert = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, EXPIRED_CERT_STRING);
+ enumInternal = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_INTERNAL_STRING);
+ enumUnknownInternalError = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_UNKNOWN_INTERNAL_ERROR_STRING);
+
+ assertNotNull("Success string match returned null", enumSuccess);
+ assertNotNull("Invalid Query Size string match returned null", enumInvalidQuerySize);
+ assertNotNull("Invalid Query ID string match returned null", enumInvalidQueryID);
+ assertNotNull("Not Supported string match returned null", enumNotSupported);
+ assertNotNull("Service Already Protected string match returned null", enumServiceAlreadyProtected);
+ assertNotNull("Service Not Protected string match returned null", enumServiceNotProtected);
+ assertNotNull("Decryption Failed string match returned null", enumDecryptionFailed);
+ assertNotNull("Encryption Failed string match returned null", enumEncryptionFailed);
+ assertNotNull("SSL Invalid Data string match returned null", enumSSLInvalidData);
+ assertNotNull("Handshake Failed string match returned null", enumHandshakeFailed);
+ assertNotNull("Invalid Cert string match returned null", enumInvalidCert);
+ assertNotNull("Expired Cert string match returned null", enumExpiredCert);
+ assertNotNull("Internal string match returned null", enumInternal);
+ assertNotNull("Unknown Internal string match returned null", enumUnknownInternalError);
+ } catch (NullPointerException exception) {
+ fail("Null enum list throws NullPointerException.");
+ }
+ }
+
+ public void testInvalidEnum() {
+ final byte INVALID_BYTE = (byte) 0xAB;
+ final String INVALID_STRING = "Invalid";
+
+ try {
+ SecurityQueryErrorCode enumInvalid = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, INVALID_BYTE);
+ assertNull("Invalid byte match didn't return null", enumInvalid);
+
+ enumInvalid = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, INVALID_STRING);
+ assertNull("Invalid byte match didn't return null", enumInvalid);
+ } catch (IllegalArgumentException exception) {
+ fail("Invalid enum throws IllegalArgumentException.");
+ }
+ }
+
+ public void testNullEnum() {
+ try {
+ SecurityQueryErrorCode enumNull = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, null);
+ assertNull("Null lookup returns a value", enumNull);
+ } catch (NullPointerException exception) {
+ fail("Null string throws NullPointerException.");
+ }
+ }
+
+ public void testListEnum() {
+ Vector<SecurityQueryErrorCode> enumTestList = new Vector<>();
+ enumTestList.add(SecurityQueryErrorCode.ERROR_SUCCESS);
+ enumTestList.add(SecurityQueryErrorCode.ERROR_INVALID_QUERY_SIZE);
+ enumTestList.add(SecurityQueryErrorCode.ERROR_INVALID_QUERY_ID);
+ enumTestList.add(SecurityQueryErrorCode.ERROR_NOT_SUPPORTED);
+ enumTestList.add(SecurityQueryErrorCode.ERROR_SERVICE_ALREADY_PROTECTED);
+ enumTestList.add(SecurityQueryErrorCode.ERROR_SERVICE_NOT_PROTECTED);
+ enumTestList.add(SecurityQueryErrorCode.ERROR_DECRYPTION_FAILED);
+ enumTestList.add(SecurityQueryErrorCode.ERROR_ENCRYPTION_FAILED);
+ enumTestList.add(SecurityQueryErrorCode.ERROR_SSL_INVALID_DATA);
+ enumTestList.add(SecurityQueryErrorCode.ERROR_HANDSHAKE_FAILED);
+ enumTestList.add(SecurityQueryErrorCode.INVALID_CERT);
+ enumTestList.add(SecurityQueryErrorCode.EXPIRED_CERT);
+ enumTestList.add(SecurityQueryErrorCode.ERROR_INTERNAL);
+ enumTestList.add(SecurityQueryErrorCode.ERROR_UNKNOWN_INTERNAL_ERROR);
+
+ assertTrue("List does not match enum test list.",
+ list.containsAll(enumTestList) &&
+ enumTestList.containsAll(list));
+
+ SecurityQueryErrorCode[] enumValueArray = SecurityQueryErrorCode.values();
+ SecurityQueryErrorCode[] enumTestArray = {
+ SecurityQueryErrorCode.ERROR_SUCCESS,
+ SecurityQueryErrorCode.ERROR_INVALID_QUERY_SIZE,
+ SecurityQueryErrorCode.ERROR_INVALID_QUERY_ID,
+ SecurityQueryErrorCode.ERROR_NOT_SUPPORTED,
+ SecurityQueryErrorCode.ERROR_SERVICE_ALREADY_PROTECTED,
+ SecurityQueryErrorCode.ERROR_SERVICE_NOT_PROTECTED,
+ SecurityQueryErrorCode.ERROR_DECRYPTION_FAILED,
+ SecurityQueryErrorCode.ERROR_ENCRYPTION_FAILED,
+ SecurityQueryErrorCode.ERROR_SSL_INVALID_DATA,
+ SecurityQueryErrorCode.ERROR_HANDSHAKE_FAILED,
+ SecurityQueryErrorCode.INVALID_CERT,
+ SecurityQueryErrorCode.EXPIRED_CERT,
+ SecurityQueryErrorCode.ERROR_INTERNAL,
+ SecurityQueryErrorCode.ERROR_UNKNOWN_INTERNAL_ERROR
+ };
+ assertTrue("Array does not match enum values array.",
+ Validator.validateQueryErrorCodeArray(enumValueArray, enumTestArray));
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryIDTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryIDTests.java
new file mode 100644
index 000000000..4f73f8139
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryIDTests.java
@@ -0,0 +1,91 @@
+package com.smartdevicelink.test.protocol.enums;
+
+import com.smartdevicelink.protocol.enums.SecurityQueryID;
+import com.smartdevicelink.test.Validator;
+
+import junit.framework.TestCase;
+
+import java.util.Vector;
+
+public class SecurityQueryIDTests extends TestCase {
+
+ private Vector<SecurityQueryID> list = SecurityQueryID.getList();
+
+ public void testValidEnums() {
+ final byte[] SEND_HANDSHAKE_DATA_BYTES = {(byte) 0x00, (byte) 0x00, (byte) 0x01};
+ final String SEND_HANDSHAKE_DATA_STRING = "SEND_HANDSHAKE_DATA";
+
+ final byte[] SEND_INTERNAL_ERROR_BYTES = {(byte) 0x00, (byte) 0x00, (byte) 0x02};
+ final String SEND_INTERNAL_ERROR_STRING = "SEND_INTERNAL_ERROR";
+
+ final byte[] INVALID_QUERY_ID_BYTES = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
+ final String INVALID_QUERY_ID_STRING = "INVALID_QUERY_ID";
+
+ try {
+ assertNotNull("QueryID list returned null", list);
+
+ SecurityQueryID enumHandshakeData = (SecurityQueryID) SecurityQueryID.get(list, SEND_HANDSHAKE_DATA_BYTES);
+ SecurityQueryID enumInternalError = (SecurityQueryID) SecurityQueryID.get(list, SEND_INTERNAL_ERROR_BYTES);
+ SecurityQueryID enumInvalidSecurityQueryId = (SecurityQueryID) SecurityQueryID.get(list, INVALID_QUERY_ID_BYTES);
+
+ assertNotNull("Send Handshake Data byte match returned null", enumHandshakeData);
+ assertNotNull("Send Internal Error byte match returned null", enumInternalError);
+ assertNotNull("Send Invalid QueryID byte match returned null", enumInvalidSecurityQueryId);
+
+ enumHandshakeData = (SecurityQueryID) SecurityQueryID.get(list, SEND_HANDSHAKE_DATA_STRING);
+ enumInternalError = (SecurityQueryID) SecurityQueryID.get(list, SEND_INTERNAL_ERROR_STRING);
+ enumInvalidSecurityQueryId = (SecurityQueryID) SecurityQueryID.get(list, INVALID_QUERY_ID_STRING);
+
+ assertNotNull("Send Handshake Data string match returned null", enumHandshakeData);
+ assertNotNull("Send Internal Error string match returned null", enumInternalError);
+ assertNotNull("Send Invalid QueryID string match returned null", enumInvalidSecurityQueryId);
+ } catch(NullPointerException exception) {
+ fail("Null enum list throws NullPointerException.");
+ }
+ }
+
+ public void testInvalidEnum() {
+
+ final byte[] INVALID_BYTE_ARRAY = {(byte) 0xAB, (byte) 0xAB, (byte) 0xAB};
+ final String INVALID_STRING = "Invalid";
+
+ try {
+ SecurityQueryID enumInvalid = (SecurityQueryID) SecurityQueryID.get(list, INVALID_BYTE_ARRAY);
+ assertNull("Invalid byte[] match didn't return null", enumInvalid);
+
+ enumInvalid = (SecurityQueryID) SecurityQueryID.get(list, INVALID_STRING);
+ assertNull("Invalid string match didn't return null", enumInvalid);
+ } catch (IllegalArgumentException exception) {
+ fail("Invalid enum throws IllegalArgumentException.");
+ }
+ }
+
+ public void testNullEnum() {
+ try {
+ SecurityQueryID enumNull = (SecurityQueryID) SecurityQueryID.get(list, (String) null);
+ assertNull("Null lookup returns a null string value", enumNull);
+
+ enumNull = (SecurityQueryID) SecurityQueryID.get(list, (byte[]) null);
+ assertNull("Null lookup returns a null byte[] value", enumNull);
+ }catch (NullPointerException exception) {
+ fail("Null string throws NullPointerException.");
+ }
+ }
+
+ public void testListEnum() {
+ Vector<SecurityQueryID> enumTestList = new Vector<>();
+ enumTestList.add(SecurityQueryID.SEND_HANDSHAKE_DATA);
+ enumTestList.add(SecurityQueryID.SEND_INTERNAL_ERROR);
+ enumTestList.add(SecurityQueryID.INVALID_QUERY_ID);
+
+ assertTrue("List does not match enum test list.",
+ list.containsAll(enumTestList) &&
+ enumTestList.containsAll(list));
+
+ SecurityQueryID[] enumValueArray = SecurityQueryID.values();
+ SecurityQueryID[] enumTestArray = {SecurityQueryID.SEND_HANDSHAKE_DATA, SecurityQueryID.SEND_INTERNAL_ERROR, SecurityQueryID.INVALID_QUERY_ID};
+ assertTrue("Array does not match enum values array.",
+ Validator.validateQueryIDArray(enumValueArray, enumTestArray));
+ }
+
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryTypeTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryTypeTests.java
new file mode 100644
index 000000000..6835da946
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryTypeTests.java
@@ -0,0 +1,97 @@
+package com.smartdevicelink.test.protocol.enums;
+
+import com.smartdevicelink.protocol.enums.SecurityQueryType;
+import com.smartdevicelink.test.Validator;
+
+import junit.framework.TestCase;
+
+import java.util.Vector;
+
+public class SecurityQueryTypeTests extends TestCase {
+
+ private Vector<SecurityQueryType> list = SecurityQueryType.getList();
+
+ public void testValidEnums() {
+ final byte REQUEST_BYTE = (byte) 0x00;
+ final String REQUEST_STRING = "REQUEST";
+
+ final byte RESPONSE_BYTE = (byte) 0x10;
+ final String RESPONSE_STRING = "RESPONSE";
+
+ final byte NOTIFICATION_BYTE = (byte) 0x20;
+ final String NOTIFICATION_STRING = "NOTIFICATION";
+
+ final byte INVALID_QUERY_TYPE_BYTE = (byte) 0xFF;
+ final String INVALID_QUERY_TYPE_STRING = "INVALID_QUERY_TYPE";
+
+ try {
+ assertNotNull("QueryType list returned null", list);
+
+ SecurityQueryType enumRequest = (SecurityQueryType) SecurityQueryType.get(list, REQUEST_BYTE);
+ SecurityQueryType enumResponse = (SecurityQueryType) SecurityQueryType.get(list, RESPONSE_BYTE);
+ SecurityQueryType enumNotification = (SecurityQueryType) SecurityQueryType.get(list, NOTIFICATION_BYTE);
+ SecurityQueryType enumInvalidSecurityQueryType = (SecurityQueryType) SecurityQueryType.get(list, INVALID_QUERY_TYPE_BYTE);
+
+ assertNotNull("Request byte match returned null", enumRequest);
+ assertNotNull("Response byte match returned null", enumResponse);
+ assertNotNull("Notification byte match returned null", enumNotification);
+ assertNotNull("Invalid Query Type byte match returned null", enumInvalidSecurityQueryType);
+
+ enumRequest = (SecurityQueryType) SecurityQueryType.get(list, REQUEST_STRING);
+ enumResponse = (SecurityQueryType) SecurityQueryType.get(list, RESPONSE_STRING);
+ enumNotification = (SecurityQueryType) SecurityQueryType.get(list, NOTIFICATION_STRING);
+ enumInvalidSecurityQueryType = (SecurityQueryType) SecurityQueryType.get(list, INVALID_QUERY_TYPE_STRING);
+
+ assertNotNull("Request string match returned null", enumRequest);
+ assertNotNull("Response string match returned null", enumResponse);
+ assertNotNull("Notification string match returned null", enumNotification);
+ assertNotNull("Invalid Query string byte match returned null", enumInvalidSecurityQueryType);
+
+
+ }catch (NullPointerException exception) {
+ fail("Null enum list throws NullPointerException.");
+ }
+ }
+
+ public void testInvalidEnum() {
+
+ final byte INVALID_BYTE = (byte) 0xAB;
+ final String INVALID_STRING = "Invalid";
+
+ try {
+ SecurityQueryType enumInvalid = (SecurityQueryType) SecurityQueryType.get(list, INVALID_BYTE);
+ assertNull("Invalid byte match didn't return null", enumInvalid);
+
+ enumInvalid = (SecurityQueryType) SecurityQueryType.get(list, INVALID_STRING);
+ assertNull("Invalid string match didn't return null", enumInvalid);
+ } catch (IllegalArgumentException exception) {
+ fail("Invalid enum throws IllegalArgumentException.");
+ }
+ }
+
+ public void testNullEnum() {
+ try {
+ SecurityQueryType enumNull = (SecurityQueryType) SecurityQueryType.get(list, null);
+ assertNull("Null lookup returns a value", enumNull);
+ } catch (NullPointerException exception) {
+ fail("Null string throws NullPointerException.");
+ }
+ }
+
+ public void testListEnum() {
+ Vector<SecurityQueryType> enumTestList = new Vector<>();
+ enumTestList.add(SecurityQueryType.REQUEST);
+ enumTestList.add(SecurityQueryType.RESPONSE);
+ enumTestList.add(SecurityQueryType.NOTIFICATION);
+ enumTestList.add(SecurityQueryType.INVALID_QUERY_TYPE);
+
+ assertTrue("List does not match enum test list.",
+ list.containsAll(enumTestList) &&
+ enumTestList.containsAll(list));
+
+ SecurityQueryType[] enumValueArray = SecurityQueryType.values();
+ SecurityQueryType[] enumTestArray = {SecurityQueryType.REQUEST, SecurityQueryType.RESPONSE, SecurityQueryType.NOTIFICATION, SecurityQueryType.INVALID_QUERY_TYPE};
+ assertTrue("Array does not match enum values array.",
+ Validator.validateQueryTypeArray(enumValueArray, enumTestArray));
+ }
+}
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 4a60fa2a4..0aae71751 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,7 +42,6 @@ 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;
@@ -55,7 +54,6 @@ 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;
@@ -63,14 +61,12 @@ 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;
import java.util.List;
@@ -94,18 +90,14 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
WindowCapability defaultMainWindowCapability;
String displayName;
SystemContext currentSystemContext;
- HashSet<ChoiceCell> preloadedChoices, pendingPreloadChoices;
- ChoiceSet pendingPresentationSet;
+ HashSet<ChoiceCell> preloadedChoices;
// We will pass operations into this to be completed
final Queue transactionQueue;
- Task pendingPresentOperation;
PresentKeyboardOperation currentlyPresentedKeyboardOperation;
- int nextChoiceId;
int nextCancelId;
- final int choiceCellIdMin = 1;
private final int choiceCellCancelIdMin = 101;
private final int choiceCellCancelIdMax = 200;
boolean isVROptional;
@@ -126,8 +118,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
// setting/instantiating class vars
this.fileManager = new WeakReference<>(fileManager);
preloadedChoices = new HashSet<>();
- pendingPreloadChoices = new HashSet<>();
- nextChoiceId = choiceCellIdMin;
nextCancelId = choiceCellCancelIdMin;
isVROptional = false;
@@ -151,10 +141,8 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
currentSystemContext = null;
defaultMainWindowCapability = null;
- pendingPresentationSet = null;
- pendingPresentOperation = null;
+ preloadedChoices = null;
isVROptional = false;
- nextChoiceId = choiceCellIdMin;
nextCancelId = choiceCellCancelIdMin;
// remove listeners
@@ -191,6 +179,10 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
transactionQueue.add(checkChoiceVR, false);
}
+ interface ChoicesOperationCompletionListener {
+ void onComplete(boolean success, HashSet<ChoiceCell> loadedChoiceCells);
+ }
+
/**
* Preload choices to improve performance while presenting a choice set at a later time
*
@@ -203,12 +195,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
return;
}
- LinkedHashSet<ChoiceCell> mutableChoicesToUpload = getChoicesToBeUploadedWithArray(choices);
-
- mutableChoicesToUpload.removeAll(preloadedChoices);
- mutableChoicesToUpload.removeAll(pendingPreloadChoices);
-
- final LinkedHashSet<ChoiceCell> choicesToUpload = (LinkedHashSet<ChoiceCell>) mutableChoicesToUpload.clone();
+ final LinkedHashSet<ChoiceCell> choicesToUpload = new LinkedHashSet<>(choices);
if (choicesToUpload.size() == 0) {
if (listener != null) {
@@ -217,29 +204,17 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
return;
}
- updateIdsOnChoices(choicesToUpload);
-
- // Add the preload cells to the pending preload choices
- pendingPreloadChoices.addAll(choicesToUpload);
-
if (fileManager.get() != null) {
- PreloadChoicesOperation preloadChoicesOperation = new PreloadChoicesOperation(internalInterface, fileManager.get(), displayName, defaultMainWindowCapability, isVROptional, choicesToUpload, new BaseChoiceSetManager.ChoiceSetCompletionListener() {
+ PreloadPresentChoicesOperation preloadChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager.get(), displayName, defaultMainWindowCapability, isVROptional, choicesToUpload, this.preloadedChoices, new ChoicesOperationCompletionListener() {
@Override
- public void onComplete(boolean success, HashSet<ChoiceCell> failedChoiceCells) {
- if (success) {
- preloadedChoices.addAll(choicesToUpload);
- pendingPreloadChoices.removeAll(choicesToUpload);
- if (listener != null) {
- listener.onComplete(true);
- }
- } else {
- DebugTool.logError(TAG, "There was an error pre loading choice cells");
- choicesToUpload.removeAll(failedChoiceCells);
- preloadedChoices.addAll(choicesToUpload);
- pendingPreloadChoices.removeAll(choicesToUpload);
- if (listener != null) {
- listener.onComplete(false);
+ public void onComplete(boolean success, HashSet<ChoiceCell> loadedCells) {
+ preloadedChoices = loadedCells;
+ updatePendingTasksWithCurrentPreloads();
+ if (listener != null) {
+ if (!success) {
+ DebugTool.logError(TAG, "There was an error pre loading choice cells");
}
+ listener.onComplete(success);
}
}
});
@@ -262,45 +237,15 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
return;
}
- // Find cells to be deleted that are already uploaded or are pending upload
- final HashSet<ChoiceCell> cellsToBeDeleted = choicesToBeDeletedWithArray(choices);
- HashSet<ChoiceCell> cellsToBeRemovedFromPending = choicesToBeRemovedFromPendingWithArray(choices);
- // If choices are deleted that are already uploaded or pending and are used by a pending presentation, cancel it and send an error
- HashSet<ChoiceCell> pendingPresentationChoices = new HashSet<>();
- if (pendingPresentationSet != null && pendingPresentationSet.getChoices() != null) {
- pendingPresentationChoices.addAll(pendingPresentationSet.getChoices());
- }
-
- if (pendingPresentOperation != null && pendingPresentOperation.getState() != Task.CANCELED && pendingPresentOperation.getState() != Task.FINISHED && (cellsToBeDeleted.retainAll(pendingPresentationChoices) || cellsToBeRemovedFromPending.retainAll(pendingPresentationChoices))) {
- pendingPresentOperation.cancelTask();
- DebugTool.logWarning(TAG, "Attempting to delete choice cells while there is a pending presentation operation. Pending presentation cancelled.");
- pendingPresentOperation = null;
- }
-
- // Remove cells from pending and delete choices
- pendingPresentationChoices.removeAll(cellsToBeRemovedFromPending);
- for (Task operation : transactionQueue.getTasksAsList()) {
- if (!(operation instanceof PreloadChoicesOperation)) {
- continue;
- }
- ((PreloadChoicesOperation) operation).removeChoicesFromUpload(cellsToBeRemovedFromPending);
- }
-
- // Find Choices to delete
- if (cellsToBeDeleted.size() == 0) {
- DebugTool.logInfo(TAG, "Cells to be deleted size == 0");
- return;
- }
- findIdsOnChoices(cellsToBeDeleted);
-
- DeleteChoicesOperation deleteChoicesOperation = new DeleteChoicesOperation(internalInterface, cellsToBeDeleted, new CompletionListener() {
+ DeleteChoicesOperation deleteChoicesOperation = new DeleteChoicesOperation(internalInterface, new HashSet<>(choices), preloadedChoices, new ChoicesOperationCompletionListener() {
@Override
- public void onComplete(boolean success) {
+ public void onComplete(boolean success, HashSet<ChoiceCell> updatedLoadedChoiceCells) {
if (!success) {
DebugTool.logError(TAG, "Failed to delete choices");
return;
}
- preloadedChoices.removeAll(cellsToBeDeleted);
+ preloadedChoices = updatedLoadedChoiceCells;
+ updatePendingTasksWithCurrentPreloads();
}
});
transactionQueue.add(deleteChoicesOperation, false);
@@ -325,63 +270,56 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
return;
}
- if (this.pendingPresentationSet != null && pendingPresentOperation != null) {
- pendingPresentOperation.cancelTask();
- DebugTool.logWarning(TAG, "Presenting a choice set while one is currently presented. Cancelling previous and continuing");
- }
-
- this.pendingPresentationSet = choiceSet;
- preloadChoices(this.pendingPresentationSet.getChoices(), new CompletionListener() {
- @Override
- public void onComplete(boolean success) {
- if (!success) {
- choiceSet.getChoiceSetSelectionListener().onError("There was an error pre-loading choice set choices");
- } else {
- sendPresentOperation(keyboardListener, mode);
- }
- }
- });
+ sendPresentOperation(choiceSet, keyboardListener, mode);
}
- private void sendPresentOperation(KeyboardListener keyboardListener, InteractionMode mode) {
+ private void sendPresentOperation(final ChoiceSet choiceSet, KeyboardListener keyboardListener, InteractionMode mode) {
if (mode == null) {
mode = InteractionMode.MANUAL_ONLY;
}
- findIdsOnChoiceSet(pendingPresentationSet);
-
// Pass back the information to the developer
- ChoiceSetSelectionListener privateChoiceListener = new ChoiceSetSelectionListener() {
+ ChoiceSetSelectionListener listener = new ChoiceSetSelectionListener() {
@Override
public void onChoiceSelected(ChoiceCell choiceCell, TriggerSource triggerSource, int rowIndex) {
- if (pendingPresentationSet != null && pendingPresentationSet.getChoiceSetSelectionListener() != null) {
- pendingPresentationSet.getChoiceSetSelectionListener().onChoiceSelected(choiceCell, triggerSource, rowIndex);
+ if (choiceSet != null && choiceSet.getChoiceSetSelectionListener() != null) {
+ choiceSet.getChoiceSetSelectionListener().onChoiceSelected(choiceCell, triggerSource, rowIndex);
}
}
@Override
public void onError(String error) {
- if (pendingPresentationSet != null && pendingPresentationSet.getChoiceSetSelectionListener() != null) {
- pendingPresentationSet.getChoiceSetSelectionListener().onError(error);
+ if (choiceSet != null && choiceSet.getChoiceSetSelectionListener() != null) {
+ if (error != null) {
+ choiceSet.getChoiceSetSelectionListener().onError(error);
+ } else if (getState() == SHUTDOWN) {
+ choiceSet.getChoiceSetSelectionListener().onError("Incorrect State");
+ } else {
+ DebugTool.logError(TAG, "Present finished but an unhandled state occurred and callback failed");
+ choiceSet.getChoiceSetSelectionListener().onError("callback failed");
+ }
}
}
};
- PresentChoiceSetOperation presentOp;
+ PreloadPresentChoicesOperation presentOp;
- if (keyboardListener == null) {
- // Non-searchable choice set
- DebugTool.logInfo(TAG, "Creating non-searchable choice set");
- presentOp = new PresentChoiceSetOperation(internalInterface, pendingPresentationSet, mode, null, null, privateChoiceListener, getNextCancelId());
+ if (fileManager.get() != null) {
+ if (keyboardListener == null) {
+ // Non-searchable choice set
+ DebugTool.logInfo(TAG, "Creating non-searchable choice set");
+ presentOp = new PreloadPresentChoicesOperation(internalInterface, fileManager.get(), choiceSet, mode, null, null, getNextCancelId(), displayName, defaultMainWindowCapability, isVROptional, this.preloadedChoices, null, listener);
+ } else {
+ // Searchable choice set
+ DebugTool.logInfo(TAG, "Creating searchable choice set");
+ presentOp = new PreloadPresentChoicesOperation(internalInterface, this.fileManager.get(), choiceSet, mode, keyboardConfiguration, keyboardListener, getNextCancelId(), displayName, defaultMainWindowCapability, isVROptional, this.preloadedChoices, null, listener);
+ }
+
+ transactionQueue.add(presentOp, false);
} else {
- // Searchable choice set
- DebugTool.logInfo(TAG, "Creating searchable choice set");
- presentOp = new PresentChoiceSetOperation(internalInterface, pendingPresentationSet, mode, keyboardConfiguration, keyboardListener, privateChoiceListener, getNextCancelId());
+ DebugTool.logError(TAG, "File Manager was null in preload choice operation");
}
-
- transactionQueue.add(presentOp, false);
- pendingPresentOperation = presentOp;
}
/**
@@ -403,12 +341,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
return null;
}
- if (pendingPresentationSet != null && pendingPresentOperation != null) {
- pendingPresentOperation.cancelTask();
- pendingPresentationSet = null;
- DebugTool.logWarning(TAG, "There is a current or pending choice set, cancelling and continuing.");
- }
-
customKeyboardConfig = createValidKeyboardConfigurationBasedOnKeyboardCapabilitiesFromConfiguration(customKeyboardConfig);
if (customKeyboardConfig == null) {
if (this.keyboardConfiguration != null) {
@@ -424,7 +356,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
PresentKeyboardOperation keyboardOp = new PresentKeyboardOperation(internalInterface, keyboardConfiguration, initialText, customKeyboardConfig, listener, keyboardCancelID);
currentlyPresentedKeyboardOperation = keyboardOp;
transactionQueue.add(keyboardOp, false);
- pendingPresentOperation = keyboardOp;
return keyboardCancelID;
}
@@ -530,63 +461,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
// CHOICE SET MANAGEMENT HELPERS
- HashSet<ChoiceCell> choicesToBeDeletedWithArray(List<ChoiceCell> choices) {
- HashSet<ChoiceCell> choicesSet = new HashSet<>(choices);
- choicesSet.retainAll(this.preloadedChoices);
- return choicesSet;
- }
-
- HashSet<ChoiceCell> choicesToBeRemovedFromPendingWithArray(List<ChoiceCell> choices) {
- HashSet<ChoiceCell> choicesSet = new HashSet<>(choices);
- choicesSet.retainAll(this.pendingPreloadChoices);
- return choicesSet;
- }
-
- /**
- * Checks if 2 or more cells have the same text/title. In case this condition is true, this function will handle the presented issue by adding "(count)".
- * 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(List<ChoiceCell> choices) {
- HashMap<String, Integer> dictCounter = new HashMap<>();
-
- for (ChoiceCell cell : choices) {
- String cellName = cell.getText();
- Integer counter = dictCounter.get(cellName);
-
- if (counter != null) {
- dictCounter.put(cellName, ++counter);
- cell.setUniqueText(cell.getText() + " (" + counter + ")");
- } else {
- dictCounter.put(cellName, 1);
- }
- }
- }
-
- 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;
@@ -599,71 +473,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
return clone;
}
- private LinkedHashSet<ChoiceCell> getChoicesToBeUploadedWithArray(List<ChoiceCell> choices) {
- // 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))) {
- // 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);
- }
- 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) {
- for (ChoiceCell cell : choices) {
- cell.setChoiceId(this.nextChoiceId);
- this.nextChoiceId++;
- }
- }
-
- private void findIdsOnChoiceSet(ChoiceSet choiceSet) {
- findIdsOnChoices(new HashSet<>(choiceSet.getChoices()));
- }
-
- private void findIdsOnChoices(HashSet<ChoiceCell> choices) {
- for (ChoiceCell cell : choices) {
- ChoiceCell uploadCell = null;
- if (pendingPreloadChoices.contains(cell)) {
- uploadCell = findIfPresent(cell, pendingPreloadChoices);
- } else if (preloadedChoices.contains(cell)) {
- uploadCell = findIfPresent(cell, preloadedChoices);
- }
- if (uploadCell != null) {
- cell.setChoiceId(uploadCell.getChoiceId());
- }
- }
- }
-
ChoiceCell findIfPresent(ChoiceCell cell, HashSet<ChoiceCell> set) {
if (set.contains(cell)) {
for (ChoiceCell setCell : set) {
@@ -674,12 +483,20 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
return null;
}
- /**
- * Interface that sends a list of ChoiceCells that failed to preload, to allow BaseChoioceSetManager
- * to stop keeping track of them for their onButtonEventListener
- */
- interface ChoiceSetCompletionListener {
- void onComplete(boolean success, HashSet<ChoiceCell> failedChoiceCells);
+ private void updatePendingTasksWithCurrentPreloads() {
+ for (Task task : this.transactionQueue.getTasksAsList()) {
+ if (task.getState() == Task.IN_PROGRESS || task.getState() == Task.CANCELED) {
+ continue;
+ }
+
+ if (task instanceof PreloadPresentChoicesOperation) {
+ PreloadPresentChoicesOperation preloadOp = (PreloadPresentChoicesOperation) task;
+ preloadOp.setLoadedCells(this.preloadedChoices);
+ } else if (task instanceof DeleteChoicesOperation) {
+ DeleteChoicesOperation deleteOp = (DeleteChoicesOperation) task;
+ deleteOp.setLoadedCells(this.preloadedChoices);
+ }
+ }
}
// LISTENERS
@@ -751,14 +568,6 @@ 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();
@@ -776,8 +585,9 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
}
}
- HashSet<ChoiceCell> uniqueChoiceCells = new HashSet<>();
HashSet<String> uniqueVoiceCommands = new HashSet<>();
+ HashSet<ChoiceCell> uniqueChoiceCells = new HashSet<>();
+
int allVoiceCommandsCount = 0;
int choiceCellWithVoiceCommandCount = 0;
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCell.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCell.java
index 7d59bf555..46d478000 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCell.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCell.java
@@ -41,10 +41,10 @@ import java.util.ArrayList;
import java.util.List;
public class ChoiceCell implements Cloneable{
- private String text, secondaryText, tertiaryText, uniqueText;
+ private String text, secondaryText, tertiaryText;
private List<String> voiceCommands;
private SdlArtwork artwork, secondaryArtwork;
- private Integer choiceId;
+ private Integer choiceId, uniqueTextId;
/**
* MAX ID for cells - Cannot use Integer.MAX_INT as the value is too high.
@@ -58,7 +58,7 @@ public class ChoiceCell implements Cloneable{
*/
public ChoiceCell(@NonNull String text) {
setText(text);
- setUniqueText(text);
+ setUniqueTextId(1);
setChoiceId(MAX_ID);
}
@@ -71,7 +71,7 @@ public class ChoiceCell implements Cloneable{
*/
public ChoiceCell(@NonNull String text, List<String> voiceCommands, SdlArtwork artwork) {
setText(text);
- setUniqueText(text);
+ setUniqueTextId(1);
setVoiceCommands(voiceCommands);
setArtwork(artwork);
setChoiceId(MAX_ID);
@@ -91,7 +91,7 @@ public class ChoiceCell implements Cloneable{
setText(text);
setSecondaryText(secondaryText);
setTertiaryText(tertiaryText);
- setUniqueText(text);
+ setUniqueTextId(1);
setVoiceCommands(voiceCommands);
setArtwork(artwork);
setSecondaryArtwork(secondaryArtwork);
@@ -238,20 +238,28 @@ public class ChoiceCell implements Cloneable{
* Attempting to use cells that are exactly the same (all text and artwork fields are the same)
* will not cause this to be used. This will not be used when connected to modules supporting RPC 7.1+.
*
- * @param uniqueText - the uniqueText to be used in place of primaryText when core does not support identical names for ChoiceSets
+ * @param uniqueTextId - the uniqueTextId to be appended to primaryText when core does not support identical names for ChoiceSets
*/
- void setUniqueText(String uniqueText) {
- this.uniqueText = uniqueText;
+ void setUniqueTextId(Integer uniqueTextId) {
+ this.uniqueTextId = uniqueTextId;
}
/**
* NOTE: USED INTERNALLY
- * Get the uniqueText that was used in place of primaryText
+ * Get the uniqueTextId that was used to append to primaryText
*
- * @return the uniqueText for this Choice Cell
+ * @return the uniqueTextId for this Choice Cell
*/
+ Integer getUniqueTextId() {
+ return uniqueTextId;
+ }
+
String getUniqueText() {
- return uniqueText;
+ if (this.uniqueTextId != 1) {
+ return this.text + " (" + this.uniqueTextId + ")";
+ } else {
+ return this.text;
+ }
}
@Override
@@ -292,7 +300,7 @@ public class ChoiceCell implements Cloneable{
@NonNull
public String toString() {
return "ChoiceCell: ID: " + this.choiceId + " Text: " + text + " - Secondary Text: " + secondaryText + " - Tertiary Text: " + tertiaryText + " " +
- (text.equals(uniqueText) ? "" : "| Unique Text: " + uniqueText) + " | Artwork Names: " + ((getArtwork() == null || getArtwork().getName() == null) ? "Primary Art null" : getArtwork().getName())
+ (uniqueTextId == 1 ? "" : "| Unique Text: " + uniqueTextId) + " | Artwork Names: " + ((getArtwork() == null || getArtwork().getName() == null) ? "Primary Art null" : getArtwork().getName())
+ " Secondary Art - " + ((getSecondaryArtwork() == null || getSecondaryArtwork().getName() == null) ? "Secondary Art null" : getSecondaryArtwork().getName()) +
" | Voice Commands Size: " + ((getVoiceCommands() == null) ? 0 : getVoiceCommands().size());
}
@@ -315,6 +323,9 @@ public class ChoiceCell implements Cloneable{
if (this.voiceCommands != null) {
clone.voiceCommands = new ArrayList<>(voiceCommands);
}
+ if (this.uniqueTextId != null) {
+ clone.uniqueTextId = this.uniqueTextId;
+ }
return clone;
} catch (CloneNotSupportedException e) {
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java
index 53f209eae..a3814c1d2 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java
@@ -36,7 +36,6 @@
package com.smartdevicelink.managers.screen.choiceset;
import com.livio.taskmaster.Task;
-import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.proxy.RPCResponse;
import com.smartdevicelink.proxy.rpc.DeleteInteractionChoiceSet;
@@ -51,25 +50,37 @@ import java.util.List;
class DeleteChoicesOperation extends Task {
private static final String TAG = "DeleteChoicesOperation";
private final WeakReference<ISdl> internalInterface;
- private final HashSet<ChoiceCell> cellsToDelete;
- private final CompletionListener completionListener;
+ private HashSet<ChoiceCell> cellsToDelete;
+ private final BaseChoiceSetManager.ChoicesOperationCompletionListener completionListener;
+ private HashSet<ChoiceCell> loadedCells;
+ private List<DeleteInteractionChoiceSet> deleteChoices;
+ private boolean completionSuccess = false;
- DeleteChoicesOperation(ISdl internalInterface, HashSet<ChoiceCell> cellsToDelete, CompletionListener completionListener) {
+ DeleteChoicesOperation(ISdl internalInterface, HashSet<ChoiceCell> cellsToDelete, HashSet<ChoiceCell> loadedCells, BaseChoiceSetManager.ChoicesOperationCompletionListener completionListener) {
super("DeleteChoicesOperation");
this.internalInterface = new WeakReference<>(internalInterface);
this.cellsToDelete = cellsToDelete;
this.completionListener = completionListener;
+ this.loadedCells = loadedCells == null ? new HashSet<ChoiceCell>() : new HashSet<>(loadedCells);
}
@Override
public void onExecute() {
DebugTool.logInfo(TAG, "Choice Operation: Executing delete choices operation");
+ updateCellsToDelete();
+ if (this.cellsToDelete == null || this.cellsToDelete.isEmpty()) {
+ if (completionListener != null) {
+ completionListener.onComplete(true, loadedCells);
+ DebugTool.logInfo(TAG, "No cells were provided to delete");
+ }
+ DeleteChoicesOperation.super.onFinished();
+ }
sendDeletions();
}
private void sendDeletions() {
- List<DeleteInteractionChoiceSet> deleteChoices = createDeleteSets();
+ deleteChoices = createDeleteSets();
if (deleteChoices.size() > 0) {
@@ -82,7 +93,7 @@ class DeleteChoicesOperation extends Task {
@Override
public void onFinished() {
if (completionListener != null) {
- completionListener.onComplete(true);
+ completionListener.onComplete(true, loadedCells);
}
DebugTool.logInfo(TAG, "Successfully deleted choices");
@@ -93,21 +104,68 @@ class DeleteChoicesOperation extends Task {
public void onResponse(int correlationId, RPCResponse response) {
if (!response.getSuccess()) {
if (completionListener != null) {
- completionListener.onComplete(false);
+ completionListener.onComplete(false, loadedCells);
}
DebugTool.logError(TAG, "Failed to delete choice: " + response.getInfo() + " | Corr ID: " + correlationId);
DeleteChoicesOperation.super.onFinished();
+ } else {
+ if (loadedCells != null) {
+ loadedCells.remove(loadedCellFromCorrelationId(deleteChoices, correlationId));
+ }
}
}
});
}
} else {
if (completionListener != null) {
- completionListener.onComplete(true);
+ completionListener.onComplete(true, this.loadedCells);
}
DebugTool.logInfo(TAG, "No Choices to delete, continue");
+ DeleteChoicesOperation.super.onFinished();
+ }
+ }
+
+ public void setLoadedCells(HashSet<ChoiceCell> loadedCells) {
+ this.loadedCells = new HashSet<>(loadedCells);
+ }
+
+ private void updateCellsToDelete() {
+ HashSet<ChoiceCell> updatedCellsToDelete = new HashSet<>(this.cellsToDelete);
+ updatedCellsToDelete.retainAll(loadedCells);
+
+ for (ChoiceCell cell : updatedCellsToDelete) {
+ for (ChoiceCell loadedCell : this.loadedCells) {
+ if (loadedCell.equals(cell)) {
+ cell.setChoiceId(loadedCell.getChoiceId());
+ }
+ }
+ }
+
+ this.cellsToDelete = updatedCellsToDelete;
+ }
+
+ private ChoiceCell loadedCellFromCorrelationId(List<DeleteInteractionChoiceSet> deleteRpcs, int correlationId) {
+
+ Integer choiceId = null;
+
+ for (DeleteInteractionChoiceSet rpc : deleteRpcs) {
+ if (rpc.getCorrelationID() == correlationId) {
+ choiceId = rpc.getInteractionChoiceSetID();
+ }
}
+
+ if (choiceId == null) {
+ return null;
+ }
+
+ for (ChoiceCell cell : this.loadedCells) {
+ if (cell.getChoiceId() == choiceId) {
+ return cell;
+ }
+ }
+
+ return null;
}
List<DeleteInteractionChoiceSet> createDeleteSets() {
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java
deleted file mode 100644
index d98ae3517..000000000
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright (c) 2019 Livio, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * Neither the name of the Livio Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * Created by brettywhite on 6/12/19 1:52 PM
- *
- */
-
-package com.smartdevicelink.managers.screen.choiceset;
-
-import androidx.annotation.NonNull;
-
-import com.livio.taskmaster.Task;
-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.file.MultipleFileCompletionListener;
-import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
-import com.smartdevicelink.proxy.RPCResponse;
-import com.smartdevicelink.proxy.rpc.Choice;
-import com.smartdevicelink.proxy.rpc.CreateInteractionChoiceSet;
-import com.smartdevicelink.proxy.rpc.Image;
-import com.smartdevicelink.proxy.rpc.WindowCapability;
-import com.smartdevicelink.proxy.rpc.enums.DisplayType;
-import com.smartdevicelink.proxy.rpc.enums.ImageFieldName;
-import com.smartdevicelink.proxy.rpc.enums.TextFieldName;
-import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener;
-import com.smartdevicelink.util.DebugTool;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-
-class PreloadChoicesOperation extends Task {
- private static final String TAG = "PreloadChoicesOperation";
- private final WeakReference<ISdl> internalInterface;
- private final WeakReference<FileManager> fileManager;
- private final WindowCapability defaultMainWindowCapability;
- private final String displayName;
- private final HashSet<ChoiceCell> cellsToUpload;
- private final BaseChoiceSetManager.ChoiceSetCompletionListener completionListener;
- private boolean isRunning;
- private final boolean isVROptional;
- private boolean choiceError = false;
-
- PreloadChoicesOperation(ISdl internalInterface, FileManager fileManager, String displayName, WindowCapability defaultMainWindowCapability,
- Boolean isVROptional, LinkedHashSet<ChoiceCell> cellsToPreload, BaseChoiceSetManager.ChoiceSetCompletionListener listener) {
- super("PreloadChoicesOperation");
- this.internalInterface = new WeakReference<>(internalInterface);
- this.fileManager = new WeakReference<>(fileManager);
- this.displayName = displayName;
- this.defaultMainWindowCapability = defaultMainWindowCapability;
- this.isVROptional = isVROptional;
- this.cellsToUpload = cellsToPreload;
- this.completionListener = listener;
- }
-
- @Override
- public void onExecute() {
- DebugTool.logInfo(TAG, "Choice Operation: Executing preload choices operation");
- preloadCellArtworks(new CompletionListener() {
- @Override
- public void onComplete(boolean success) {
- preloadCells();
- }
- });
- }
-
- void removeChoicesFromUpload(HashSet<ChoiceCell> choices) {
- if (isRunning) {
- return;
- }
- cellsToUpload.removeAll(choices);
- }
-
- private void preloadCellArtworks(@NonNull final CompletionListener listener) {
- isRunning = true;
-
- List<SdlArtwork> artworksToUpload = artworksToUpload();
-
- if (artworksToUpload.size() == 0) {
- DebugTool.logInfo(TAG, "Choice Preload: No Choice Artworks to upload");
- listener.onComplete(true);
- isRunning = false;
- return;
- }
-
- if (fileManager.get() != null) {
- fileManager.get().uploadArtworks(artworksToUpload, new MultipleFileCompletionListener() {
- @Override
- public void onComplete(Map<String, String> errors) {
- if (errors != null && errors.size() > 0) {
- DebugTool.logError(TAG, "Error uploading choice cell Artworks: " + errors.toString());
- listener.onComplete(false);
- isRunning = false;
- } else {
- DebugTool.logInfo(TAG, "Choice Artworks Uploaded");
- listener.onComplete(true);
- isRunning = false;
- }
- }
- });
- } else {
- DebugTool.logError(TAG, "File manager is null in choice preload operation");
- listener.onComplete(false);
- isRunning = false;
- }
- }
-
- private void preloadCells() {
- isRunning = true;
- final List<CreateInteractionChoiceSet> choiceRPCs = new ArrayList<>(cellsToUpload.size());
- for (ChoiceCell cell : cellsToUpload) {
- CreateInteractionChoiceSet csCell = choiceFromCell(cell);
- if (csCell != null) {
- choiceRPCs.add(csCell);
- }
- }
-
- if (choiceRPCs.size() == 0) {
- DebugTool.logError(TAG, " All Choice cells to send are null, so the choice set will not be shown");
- completionListener.onComplete(true, null);
- isRunning = false;
- return;
- }
-
- if (internalInterface.get() != null) {
- internalInterface.get().sendRPCs(choiceRPCs, new OnMultipleRequestListener() {
- final HashSet<ChoiceCell> failedChoiceCells = new HashSet<>();
-
- @Override
- public void onUpdate(int remainingRequests) {
-
- }
-
- @Override
- public void onFinished() {
- isRunning = false;
- DebugTool.logInfo(TAG, "Finished pre loading choice cells");
- completionListener.onComplete(!choiceError, !choiceError ? null : failedChoiceCells);
- choiceError = false;
- PreloadChoicesOperation.super.onFinished();
- }
-
- @Override
- public void onResponse(int correlationId, RPCResponse response) {
- if (!response.getSuccess()) {
- DebugTool.logError(TAG, "There was an error uploading a choice cell: " + response.getInfo() + " resultCode: " + response.getResultCode());
- choiceError = true;
- for (CreateInteractionChoiceSet choiceSet : choiceRPCs) {
- if (choiceSet.getCorrelationID() == correlationId) {
- int choiceId = choiceSet.getChoiceSet().get(0).getChoiceID();
- for (ChoiceCell cell : cellsToUpload) {
- if (cell.getChoiceId() == choiceId) {
- failedChoiceCells.add(cell);
- }
- }
- }
- }
- }
- }
- });
- } else {
- DebugTool.logError(TAG, "Internal Interface null in preload choice operation");
- isRunning = false;
- completionListener.onComplete(false, cellsToUpload);
- }
- }
-
- private CreateInteractionChoiceSet choiceFromCell(ChoiceCell cell) {
-
- List<String> vrCommands;
- if (cell.getVoiceCommands() == null) {
- vrCommands = isVROptional ? null : Collections.singletonList(String.valueOf(cell.getChoiceId()));
- } else {
- vrCommands = cell.getVoiceCommands();
- }
-
- String menuName = shouldSendChoiceText() ? cell.getUniqueText() : null;
-
- if (menuName == null) {
- DebugTool.logError(TAG, "Could not convert Choice Cell to CreateInteractionChoiceSet. It will not be shown. Cell: " + cell.toString());
- return null;
- }
-
- String secondaryText = shouldSendChoiceSecondaryText() ? cell.getSecondaryText() : null;
- String tertiaryText = shouldSendChoiceTertiaryText() ? cell.getTertiaryText() : null;
-
- Image image = shouldSendChoicePrimaryImage() && cell.getArtwork() != null ? cell.getArtwork().getImageRPC() : null;
- Image secondaryImage = shouldSendChoiceSecondaryImage() && cell.getSecondaryArtwork() != null ? cell.getSecondaryArtwork().getImageRPC() : null;
-
- Choice choice = new Choice(cell.getChoiceId(), menuName);
- choice.setVrCommands(vrCommands);
- choice.setSecondaryText(secondaryText);
- choice.setTertiaryText(tertiaryText);
- choice.setIgnoreAddingVRItems(true);
-
- if (fileManager.get() != null) {
- if (image != null && (cell.getArtwork().isStaticIcon() || fileManager.get().hasUploadedFile(cell.getArtwork()))) {
- choice.setImage(image);
- }
- if (secondaryImage != null && (cell.getSecondaryArtwork().isStaticIcon() || fileManager.get().hasUploadedFile(cell.getSecondaryArtwork()))) {
- choice.setSecondaryImage(secondaryImage);
- }
- }
-
- return new CreateInteractionChoiceSet(choice.getChoiceID(), Collections.singletonList(choice));
- }
-
- // HELPERS
- boolean shouldSendChoiceText() {
- if (this.displayName != null && this.displayName.equals(DisplayType.GEN3_8_INCH.toString())) {
- return true;
- }
- return templateSupportsTextField(TextFieldName.menuName);
- }
-
- boolean shouldSendChoiceSecondaryText() {
- return templateSupportsTextField(TextFieldName.secondaryText);
- }
-
- boolean shouldSendChoiceTertiaryText() {
- return templateSupportsTextField(TextFieldName.tertiaryText);
- }
-
- boolean shouldSendChoicePrimaryImage() {
- return templateSupportsImageField(ImageFieldName.choiceImage);
- }
-
- boolean shouldSendChoiceSecondaryImage() {
- return templateSupportsImageField(ImageFieldName.choiceSecondaryImage);
- }
-
- boolean templateSupportsTextField(TextFieldName name) {
- return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, name);
- }
-
- boolean templateSupportsImageField(ImageFieldName name) {
- return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, name);
- }
-
- List<SdlArtwork> artworksToUpload() {
- List<SdlArtwork> artworksToUpload = new ArrayList<>();
- for (ChoiceCell cell : cellsToUpload) {
- if (shouldSendChoicePrimaryImage() && fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getArtwork())) {
- artworksToUpload.add(cell.getArtwork());
- }
- if (shouldSendChoiceSecondaryImage() && fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getSecondaryArtwork())) {
- artworksToUpload.add(cell.getSecondaryArtwork());
- }
- }
- return artworksToUpload;
- }
-} \ No newline at end of file
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java
new file mode 100644
index 000000000..4c9d5a3b5
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java
@@ -0,0 +1,923 @@
+/*
+ * Copyright (c) 2021 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Created by rhenigan on 8/9/21 1:52 PM
+ *
+ */
+
+package com.smartdevicelink.managers.screen.choiceset;
+
+import androidx.annotation.NonNull;
+
+import com.livio.taskmaster.Task;
+import com.smartdevicelink.managers.CompletionListener;
+import com.smartdevicelink.managers.ISdl;
+import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName;
+import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName;
+import com.smartdevicelink.managers.file.FileManager;
+import com.smartdevicelink.managers.file.MultipleFileCompletionListener;
+import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.proxy.RPCNotification;
+import com.smartdevicelink.proxy.RPCResponse;
+import com.smartdevicelink.proxy.rpc.CancelInteraction;
+import com.smartdevicelink.proxy.rpc.Choice;
+import com.smartdevicelink.proxy.rpc.CreateInteractionChoiceSet;
+import com.smartdevicelink.proxy.rpc.Image;
+import com.smartdevicelink.proxy.rpc.KeyboardProperties;
+import com.smartdevicelink.proxy.rpc.OnKeyboardInput;
+import com.smartdevicelink.proxy.rpc.PerformInteraction;
+import com.smartdevicelink.proxy.rpc.PerformInteractionResponse;
+import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
+import com.smartdevicelink.proxy.rpc.SetGlobalProperties;
+import com.smartdevicelink.proxy.rpc.WindowCapability;
+import com.smartdevicelink.proxy.rpc.enums.DisplayType;
+import com.smartdevicelink.proxy.rpc.enums.ImageFieldName;
+import com.smartdevicelink.proxy.rpc.enums.InteractionMode;
+import com.smartdevicelink.proxy.rpc.enums.KeyboardEvent;
+import com.smartdevicelink.proxy.rpc.enums.LayoutMode;
+import com.smartdevicelink.proxy.rpc.enums.Result;
+import com.smartdevicelink.proxy.rpc.enums.TextFieldName;
+import com.smartdevicelink.proxy.rpc.enums.TriggerSource;
+import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
+import com.smartdevicelink.util.DebugTool;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+
+class PreloadPresentChoicesOperation extends Task {
+
+ private static final String TAG = "PreloadPresentChoicesOperation";
+ private final WeakReference<ISdl> internalInterface;
+ private final WeakReference<FileManager> fileManager;
+ private final WindowCapability defaultMainWindowCapability;
+ private final String displayName;
+ private final ArrayList<ChoiceCell> cellsToUpload;
+ private final BaseChoiceSetManager.ChoicesOperationCompletionListener completionListener;
+ private final ChoiceSetSelectionListener selectionListener;
+ private final boolean isVROptional;
+ private boolean choiceError = false;
+ private HashSet<ChoiceCell> loadedCells;
+ private final ChoiceSet choiceSet;
+ // Start choiceId at 1 to ensure all HMIs handle it https://github.com/smartdevicelink/generic_hmi/commit/b292fbbec095b9ce11b520d47ec95b6fcff8e247
+ private static Integer choiceId = 1;
+ private static Boolean reachedMaxIds = false;
+ private static final int MAX_CHOICE_ID = 65535;
+ private final Integer cancelID;
+ private final InteractionMode presentationMode;
+ private final KeyboardProperties originalKeyboardProperties;
+ private KeyboardProperties keyboardProperties;
+ private ChoiceCell selectedCell;
+ private TriggerSource selectedTriggerSource;
+ private boolean updatedKeyboardProperties;
+ private OnRPCNotificationListener keyboardRPCListener;
+ Integer selectedCellRow;
+ KeyboardListener keyboardListener;
+ final SdlMsgVersion sdlMsgVersion;
+ private enum SDLPreloadPresentChoicesOperationState {
+ NOT_STARTED,
+ UPLOADING_IMAGES,
+ UPLOADING_CHOICES,
+ UPDATING_KEYBOARD_PROPERTIES,
+ PRESENTING_CHOICES,
+ CANCELLING_PRESENT_CHOICES,
+ RESETTING_KEYBOARD_PROPERTIES,
+ FINISHING
+ }
+ private SDLPreloadPresentChoicesOperationState currentState;
+
+ PreloadPresentChoicesOperation(ISdl internalInterface, FileManager fileManager, String displayName, WindowCapability defaultWindowCapability,
+ Boolean isVROptional, LinkedHashSet<ChoiceCell> cellsToPreload, HashSet<ChoiceCell> loadedCells, BaseChoiceSetManager.ChoicesOperationCompletionListener listener) {
+ super("PreloadPresentChoiceOperation");
+ this.internalInterface = new WeakReference<>(internalInterface);
+ this.fileManager = new WeakReference<>(fileManager);
+ this.displayName = displayName;
+ this.defaultMainWindowCapability = defaultWindowCapability;
+ this.isVROptional = isVROptional;
+ this.cellsToUpload = new ArrayList<>(cellsToPreload);
+ this.completionListener = listener;
+ this.keyboardListener = null;
+ this.choiceSet = null;
+ this.presentationMode = null;
+ this.cancelID = null;
+ this.originalKeyboardProperties = null;
+ this.keyboardProperties = null;
+ this.selectedCellRow = null;
+ this.sdlMsgVersion = internalInterface.getSdlMsgVersion();
+ this.loadedCells = loadedCells;
+ this.currentState = SDLPreloadPresentChoicesOperationState.NOT_STARTED;
+ this.selectionListener = null;
+ }
+
+ PreloadPresentChoicesOperation(ISdl internalInterface, FileManager fileManager, ChoiceSet choiceSet, InteractionMode mode,
+ KeyboardProperties originalKeyboardProperties, KeyboardListener keyboardListener, Integer cancelID, String displayName, WindowCapability windowCapability,
+ Boolean isVROptional, HashSet<ChoiceCell> loadedCells, BaseChoiceSetManager.ChoicesOperationCompletionListener preloadListener, ChoiceSetSelectionListener listener) {
+ super("PreloadPresentChoiceOperation");
+ this.internalInterface = new WeakReference<>(internalInterface);
+ this.keyboardListener = keyboardListener;
+ this.choiceSet = choiceSet;
+ this.choiceSet.canceledListener = new ChoiceSetCanceledListener() {
+ @Override
+ public void onChoiceSetCanceled() {
+ cancelInteraction();
+ }
+ };
+ this.presentationMode = mode;
+ this.cancelID = cancelID;
+ this.originalKeyboardProperties = originalKeyboardProperties;
+ this.keyboardProperties = originalKeyboardProperties;
+ this.selectedCellRow = null;
+ this.sdlMsgVersion = internalInterface.getSdlMsgVersion();
+ this.fileManager = new WeakReference<>(fileManager);
+ this.displayName = displayName;
+ this.defaultMainWindowCapability = windowCapability;
+ this.isVROptional = isVROptional;
+ this.cellsToUpload = new ArrayList<>(choiceSet.getChoices());
+ this.completionListener = preloadListener;
+ this.selectionListener = listener;
+ this.loadedCells = loadedCells;
+ this.currentState = SDLPreloadPresentChoicesOperationState.NOT_STARTED;
+ }
+
+ @Override
+ public void onExecute() {
+ if (this.getState() == CANCELED) {
+ return;
+ }
+
+ if (this.loadedCells == null || this.loadedCells.isEmpty()) {
+ choiceId = 1;
+ reachedMaxIds = false;
+ }
+
+ DebugTool.logInfo(TAG, "Choice Operation: Executing preload choices operation");
+ // Enforce unique cells and remove cells that are already loaded
+ this.cellsToUpload.removeAll(loadedCells);
+
+ if ((this.loadedCells.size() == MAX_CHOICE_ID) && this.cellsToUpload.size() > 0) {
+ DebugTool.logError(TAG, "Choice Cells to upload exceed maximum");
+ finishOperation(false);
+ }
+
+ assignIdsToCells(this.cellsToUpload);
+ makeCellsToUploadUnique(this.cellsToUpload);
+
+ if (this.choiceSet != null) {
+ updateChoiceSet(this.choiceSet, this.loadedCells, new HashSet<>(this.cellsToUpload));
+ }
+ // Start uploading cell artworks, then cells themselves, then determine if we want to present, then update keyboard properties if necessary, then present the choice set, then revert keyboard properties if necessary
+ preloadCellArtworks(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ // If some artworks failed to upload, we are still going to try to load the cells
+ if (getState()==CANCELED || !success) {
+ finishOperation(false);
+ return;
+ }
+ preloadCells(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ if (getState()==CANCELED || !success) {
+ finishOperation(false);
+ return;
+ }
+
+ if (choiceSet == null) {
+ finishOperation(true);
+ return;
+ }
+ DebugTool.logInfo(TAG, "Choice Operation: Executing present choice set operation");
+ updateKeyboardProperties(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ if (getState()==CANCELED || !success) {
+ finishOperation(false);
+ return;
+ }
+ presentChoiceSet(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ resetKeyboardProperties(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ finishOperation(success);
+ return;
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+
+ // Preload operation methods
+ private void preloadCellArtworks(@NonNull final CompletionListener listener) {
+ this.currentState = SDLPreloadPresentChoicesOperationState.UPLOADING_IMAGES;
+
+ List<SdlArtwork> artworksToUpload = new ArrayList<>(artworksToUpload());
+
+ if (artworksToUpload.size() == 0) {
+ DebugTool.logInfo(TAG, "Choice Preload: No Choice Artworks to upload");
+ listener.onComplete(true);
+ return;
+ }
+
+ if (fileManager.get() != null) {
+ fileManager.get().uploadArtworks(artworksToUpload, new MultipleFileCompletionListener() {
+ @Override
+ public void onComplete(Map<String, String> errors) {
+ if (errors != null && errors.size() > 0) {
+ DebugTool.logError(TAG, "Error uploading choice cell Artworks: " + errors.toString());
+ listener.onComplete(false);
+ } else {
+ DebugTool.logInfo(TAG, "Choice Artworks Uploaded");
+ listener.onComplete(true);
+ }
+ }
+ });
+ } else {
+ DebugTool.logError(TAG, "File manager is null in choice preload operation");
+ listener.onComplete(false);
+ }
+ }
+
+ private void preloadCells(@NonNull final CompletionListener listener) {
+ this.currentState = SDLPreloadPresentChoicesOperationState.UPLOADING_CHOICES;
+
+ final List<CreateInteractionChoiceSet> choiceRPCs = new ArrayList<>(cellsToUpload.size());
+ for (ChoiceCell cell : cellsToUpload) {
+ CreateInteractionChoiceSet csCell = choiceFromCell(cell);
+ if (csCell != null) {
+ choiceRPCs.add(csCell);
+ }
+ }
+
+ if (choiceRPCs.size() == 0) {
+ if (choiceSet == null) {
+ DebugTool.logError(TAG, " All Choice cells to send are null, so the choice set will not be shown");
+ }
+ listener.onComplete(true);
+ return;
+ }
+
+ if (internalInterface.get() != null) {
+ internalInterface.get().sendRPCs(choiceRPCs, new OnMultipleRequestListener() {
+ @Override
+ public void onUpdate(int remainingRequests) {
+
+ }
+
+ @Override
+ public void onFinished() {
+ DebugTool.logInfo(TAG, "Finished pre loading choice cells");
+ listener.onComplete(!choiceError);
+ choiceError = false;
+ }
+
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ if (!response.getSuccess()) {
+ DebugTool.logError(TAG, "There was an error uploading a choice cell: " + response.getInfo() + " resultCode: " + response.getResultCode());
+ choiceError = true;
+ } else {
+ for (CreateInteractionChoiceSet rpc : choiceRPCs) {
+ if (correlationId == rpc.getCorrelationID()) {
+ loadedCells.add(cellFromChoiceId(rpc.getChoiceSet().get(0).getChoiceID()));
+ }
+ }
+ }
+ }
+ });
+ } else {
+ DebugTool.logError(TAG, "Internal Interface null in preload choice operation");
+ listener.onComplete(!choiceError);
+ }
+ }
+
+ private void updateKeyboardProperties(final CompletionListener listener) {
+ this.currentState = SDLPreloadPresentChoicesOperationState.UPDATING_KEYBOARD_PROPERTIES;
+ if (keyboardListener == null) {
+ if (listener != null) {
+ listener.onComplete(true);
+ }
+ return;
+ }
+
+ addListeners();
+
+ if (keyboardListener != null && choiceSet.getCustomKeyboardConfiguration() != null) {
+ keyboardProperties = choiceSet.getCustomKeyboardConfiguration();
+ updatedKeyboardProperties = true;
+ }
+
+ SetGlobalProperties setGlobalProperties = new SetGlobalProperties();
+ setGlobalProperties.setKeyboardProperties(keyboardProperties);
+ setGlobalProperties.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+
+ if (!response.getSuccess()) {
+ if (listener != null) {
+ listener.onComplete(false);
+ }
+ DebugTool.logError(TAG, "Error Setting keyboard properties in present choice set operation");
+ return;
+ }
+
+ updatedKeyboardProperties = true;
+
+ if (listener != null) {
+ listener.onComplete(true);
+ }
+ DebugTool.logInfo(TAG, "Success Setting keyboard properties in present choice set operation");
+ }
+ });
+ if (internalInterface.get() != null) {
+ internalInterface.get().sendRPC(setGlobalProperties);
+ } else {
+ DebugTool.logError(TAG, "Internal interface null - present choice set op - choice");
+ listener.onComplete(false);
+ }
+ }
+
+ void resetKeyboardProperties(final CompletionListener listener) {
+ this.currentState = SDLPreloadPresentChoicesOperationState.RESETTING_KEYBOARD_PROPERTIES;
+ if (this.keyboardListener == null || this.originalKeyboardProperties == null) {
+ if(listener != null) {
+ listener.onComplete(true);
+ finishOperation(true);
+ return;
+ }
+ }
+ SetGlobalProperties setProperties = new SetGlobalProperties();
+ setProperties.setKeyboardProperties(this.originalKeyboardProperties);
+ setProperties.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ if (response.getSuccess()) {
+ updatedKeyboardProperties = false;
+ DebugTool.logInfo(TAG, "Successfully reset choice keyboard properties to original config");
+ } else {
+ DebugTool.logError(TAG, "Failed to reset choice keyboard properties to original config " + response.getResultCode() + ", " + response.getInfo());
+ }
+ if (listener != null) {
+ listener.onComplete(response.getSuccess());
+ if (response.getSuccess()) {
+ finishOperation(true);
+ }
+ }
+ }
+ });
+
+ if (internalInterface.get() != null) {
+ internalInterface.get().sendRPC(setProperties);
+ internalInterface.get().removeOnRPCNotificationListener(FunctionID.ON_KEYBOARD_INPUT, keyboardRPCListener);
+ } else {
+ DebugTool.logError(TAG, "Internal Interface null when finishing choice keyboard reset");
+ listener.onComplete(false);
+ }
+ }
+
+ private void presentChoiceSet(final CompletionListener listener) {
+ this.currentState = SDLPreloadPresentChoicesOperationState.PRESENTING_CHOICES;
+ PerformInteraction pi = getPerformInteraction();
+ pi.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ if (!response.getSuccess()) {
+ DebugTool.logError(TAG, "Presenting Choice set failed: " + response.getInfo());
+
+ if (selectionListener != null) {
+ selectionListener.onError(response.getInfo());
+ }
+ if (listener != null) {
+ listener.onComplete(false);
+ }
+ finishOperation(false);
+ return;
+ }
+
+ PerformInteractionResponse performInteractionResponse = (PerformInteractionResponse) response;
+ setSelectedCellWithId(performInteractionResponse.getChoiceID());
+ selectedTriggerSource = performInteractionResponse.getTriggerSource();
+
+ if (selectionListener != null && selectedCell != null && selectedTriggerSource != null && selectedCellRow != null) {
+ selectionListener.onChoiceSelected(selectedCell, selectedTriggerSource, selectedCellRow);
+ if (listener != null) {
+ listener.onComplete(true);
+ }
+ } else {
+ if (listener != null) {
+ listener.onComplete(false);
+ }
+ }
+ }
+ });
+ if (internalInterface.get() != null) {
+ internalInterface.get().sendRPC(pi);
+ } else {
+ DebugTool.logError(TAG, "Internal Interface null when presenting choice set in operation");
+ if (selectionListener != null) {
+ selectionListener.onError("Internal Interface null");
+ }
+ if (listener != null) {
+ listener.onComplete(false);
+ }
+ }
+ }
+
+ private void cancelInteraction() {
+ if ((getState() == Task.FINISHED)) {
+ DebugTool.logInfo(TAG, "This operation has already finished so it can not be canceled.");
+ return;
+ } else if (getState() == Task.CANCELED) {
+ DebugTool.logInfo(TAG, "This operation has already been canceled. It will be finished at some point during the operation.");
+ return;
+ } else if ((getState() == Task.IN_PROGRESS)) {
+ if (this.currentState != SDLPreloadPresentChoicesOperationState.PRESENTING_CHOICES) {
+ DebugTool.logInfo(TAG, "Canceling the operation before a present.");
+ this.cancelTask();
+ return;
+ }else if (sdlMsgVersion.getMajorVersion() < 6) {
+ DebugTool.logWarning(TAG, "Canceling a presented choice set is not supported on this head unit");
+ this.cancelTask();
+ return;
+ }
+
+ DebugTool.logInfo(TAG, "Canceling the presented choice set interaction.");
+
+ CancelInteraction cancelInteraction = new CancelInteraction(FunctionID.PERFORM_INTERACTION.getId(), cancelID);
+ cancelInteraction.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ if (response.getSuccess()) {
+ DebugTool.logInfo(TAG, "Canceled the presented choice set " + ((response.getResultCode() == Result.SUCCESS) ? "successfully" : "unsuccessfully"));
+ } else {
+ DebugTool.logError(TAG, "Error canceling the presented choice set: " + correlationId + " with error: " + response.getInfo());
+ }
+ }
+ });
+
+ if (internalInterface.get() != null) {
+ internalInterface.get().sendRPC(cancelInteraction);
+ } else {
+ DebugTool.logError(TAG, "Internal interface null - could not send cancel interaction for choice set");
+ }
+ } else {
+ DebugTool.logInfo(TAG, "Canceling a choice set that has not yet been sent to Core");
+ this.cancelTask();
+ }
+ }
+
+ // Present Helpers
+ void setSelectedCellWithId(Integer cellId) {
+ if (choiceSet.getChoices() != null && cellId != null) {
+ List<ChoiceCell> cells = choiceSet.getChoices();
+ for (int i = 0; i < cells.size(); i++) {
+ if (cells.get(i).getChoiceId() == cellId) {
+ selectedCell = cells.get(i);
+ selectedCellRow = i;
+ return;
+ }
+ }
+ }
+ }
+
+ PerformInteraction getPerformInteraction() {
+ if (this.choiceSet == null) {
+ return new PerformInteraction();
+ }
+ PerformInteraction pi = new PerformInteraction(choiceSet.getTitle(), presentationMode, getChoiceIds());
+ pi.setInitialPrompt(choiceSet.getInitialPrompt());
+ pi.setHelpPrompt(choiceSet.getHelpPrompt());
+ pi.setTimeoutPrompt(choiceSet.getTimeoutPrompt());
+ pi.setVrHelp(choiceSet.getVrHelpList());
+ pi.setTimeout(choiceSet.getTimeout() * 1000);
+ pi.setInteractionLayout(getLayoutMode());
+ pi.setCancelID(cancelID);
+ return pi;
+ }
+
+ private void assignIdsToCells(ArrayList<ChoiceCell> cells) {
+ ArrayList<Integer> usedIds = new ArrayList<>();
+ for (ChoiceCell cell : loadedCells) {
+ usedIds.add(cell.getChoiceId());
+ }
+ Collections.sort(usedIds);
+ ArrayList<Integer> sortedUsedIds = (ArrayList<Integer>) usedIds.clone();
+
+ // Loop through the cells we need ids for. Get and assign those ids
+ for (int i = 0; i < cells.size(); i++) {
+ int cellId = nextChoiceIdBasedOnUsedIds(sortedUsedIds);
+ cells.get(i).setChoiceId(cellId);
+
+ // Insert the ids into the usedIds sorted array in the correct position
+ for (int j = 0; j < sortedUsedIds.size(); j++) {
+ if (sortedUsedIds.get(j) > cellId) {
+ sortedUsedIds.add(j, cellId);
+ break;
+ } else if (j == (sortedUsedIds.size() - 1)) {
+ sortedUsedIds.add(cellId);
+ break;
+ }
+ }
+ }
+ }
+
+ // Find the next available choice is. Takes into account the loaded cells to ensure there are not duplicates
+ // @param usedIds The already loaded cell ids
+ // @return The choice id between 0 - 65535, or Not Found if no cell ids were available
+ private int nextChoiceIdBasedOnUsedIds(ArrayList<Integer> usedIds) {
+ // Check if we are entirely full, or if we've advanced beyond the max value, loop back
+ if (choiceId == MAX_CHOICE_ID) {
+ choiceId = 1;
+ reachedMaxIds = true;
+ }
+
+ if (reachedMaxIds) {
+ // We've looped all the way around, so we need to check loaded cells
+ // Sort the set of cells by the choice id so that we can more easily check which slots are available
+ if (usedIds.size() >= (MAX_CHOICE_ID + 1)) {
+ //If we've maxed out our slots, return the max value
+ choiceId = MAX_CHOICE_ID;
+ return choiceId;
+ }
+
+ // If the last value isn't the max value, just keep grabbing towards the max value
+ int lastUsedId = usedIds.get(usedIds.size() - 1);
+ if (lastUsedId < MAX_CHOICE_ID) {
+ choiceId = lastUsedId + 1;
+ return choiceId;
+ }
+
+ // All our easy options are gone. Find and grab an empty slot from within the sorted list
+ for (int i = 0; i < usedIds.size(); i++) {
+ int loopId = usedIds.get(i);
+ if (i != loopId) {
+ // This slot is open because the cell id does not match an open sorted slot
+ choiceId = i;
+ return choiceId;
+ }
+ }
+
+ // This *shouldn't* be possible
+ choiceId = MAX_CHOICE_ID;
+ return choiceId;
+ } else {
+ // We haven't looped all the way around yet, so we'll just take the current number, then advance the item
+ return choiceId++;
+ }
+ }
+
+ // Choice Uniqueness
+ void makeCellsToUploadUnique(ArrayList<ChoiceCell> cellsToUpload) {
+ if (cellsToUpload.size() == 0) {
+ return;
+ }
+
+ ArrayList<ChoiceCell> strippedCellsToUpload = cloneChoiceCellList(cellsToUpload);
+ ArrayList<ChoiceCell> strippedLoadedCells = cloneChoiceCellList(new ArrayList<>(loadedCells));
+ boolean supportsChoiceUniqueness = !(sdlMsgVersion.getMajorVersion() < 7 || (sdlMsgVersion.getMajorVersion() == 7 && sdlMsgVersion.getMinorVersion() == 0));
+ if (supportsChoiceUniqueness) {
+ removeUnusedProperties(strippedCellsToUpload);
+ removeUnusedProperties(strippedLoadedCells);
+ }
+
+ addUniqueNamesToCells(strippedCellsToUpload, strippedLoadedCells, supportsChoiceUniqueness);
+ transferUniqueNamesFromCells(strippedCellsToUpload, cellsToUpload);
+ }
+
+ private void updateChoiceSet(ChoiceSet choiceSet, HashSet<ChoiceCell> loadedCells, HashSet<ChoiceCell> cellsToUpload) {
+ ArrayList<ChoiceCell> choiceSetCells = new ArrayList<>();
+ for (ChoiceCell cell : choiceSet.getChoices()) {
+ if (loadedCells.contains(cell) || cellsToUpload.contains(cell)) {
+ choiceSetCells.add(cell);
+ }
+ }
+ this.choiceSet.setChoices((List<ChoiceCell>) choiceSetCells.clone());
+ }
+
+ private void transferUniqueNamesFromCells(ArrayList<ChoiceCell> fromCells, ArrayList<ChoiceCell> toCells) {
+ for (int i = 0; i< fromCells.size(); i++) {
+ toCells.get(i).setUniqueTextId(fromCells.get(i).getUniqueTextId());
+ }
+ }
+
+ void removeUnusedProperties(List<ChoiceCell> choiceCells) {
+ for (ChoiceCell cell : choiceCells) {
+ // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI
+ cell.setVoiceCommands(null);
+
+ if (!shouldSendChoicePrimaryImage()) {
+ cell.setArtwork(null);
+ }
+ if (!shouldSendChoiceSecondaryText()) {
+ cell.setSecondaryText(null);
+ }
+ if (!shouldSendChoiceTertiaryText()) {
+ cell.setTertiaryText(null);
+ }
+ if (!shouldSendChoiceSecondaryImage()) {
+ cell.setSecondaryArtwork(null);
+ }
+ }
+ }
+
+ private ArrayList<ChoiceCell> cloneChoiceCellList(List<ChoiceCell> originalList) {
+ if (originalList == null) {
+ return null;
+ }
+ ArrayList<ChoiceCell> clone = new ArrayList<>();
+ for (ChoiceCell choiceCell : originalList) {
+ clone.add(choiceCell.clone());
+ }
+ return clone;
+ }
+
+ /**
+ * Checks if 2 or more cells have the same text/title. In case this condition is true, this function will handle the presented issue by adding "(count)".
+ * 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)".
+ */
+ void addUniqueNamesToCells(List<ChoiceCell> cellsToUpload, List<ChoiceCell> loadedCells, boolean supportsChoiceUniqueness) {
+ HashMap<Object, ArrayList<Integer>> dictCounter = new HashMap<>();
+
+ for (ChoiceCell loadedCell : loadedCells) {
+ Object cellKey = supportsChoiceUniqueness ? loadedCell : loadedCell.getText();
+ int cellUniqueId = loadedCell.getUniqueTextId();
+ if (dictCounter.get(cellKey) == null) {
+ ArrayList<Integer> uniqueIds = new ArrayList<>();
+ uniqueIds.add(cellUniqueId);
+ dictCounter.put(cellKey, uniqueIds);
+ } else {
+ dictCounter.get(cellKey).add(cellUniqueId);
+ }
+ }
+
+ for (ChoiceCell cell : cellsToUpload) {
+ Object cellKey = supportsChoiceUniqueness ? cell : cell.getText();
+ if (dictCounter.get(cellKey) == null) {
+ ArrayList<Integer> uniqueTextIds = new ArrayList<>();
+ uniqueTextIds.add(cell.getUniqueTextId());
+ dictCounter.put(cellKey, uniqueTextIds);
+ } else {
+ ArrayList<Integer> uniqueIds = dictCounter.get(cellKey);
+ Integer lowestMissingUniqueId = uniqueIds.get(uniqueIds.size() - 1) + 1;
+ for (int i = 1; i < dictCounter.get(cellKey).size() + 1; i++) {
+ if (i != dictCounter.get(cellKey).get(i -1)) {
+ lowestMissingUniqueId = i;
+ break;
+ }
+ }
+
+ cell.setUniqueTextId(lowestMissingUniqueId);
+ dictCounter.get(cellKey).add(cell.getUniqueTextId() - 1, cell.getUniqueTextId());
+ }
+ }
+ }
+
+ // Finding Cells
+ private ChoiceCell cellFromChoiceId(int choiceId) {
+ for (ChoiceCell cell : this.cellsToUpload) {
+ if (cell.getChoiceId() == choiceId) {
+ return cell;
+ }
+ }
+ return null;
+ }
+
+ // Assembling Choice RPCs
+ private CreateInteractionChoiceSet choiceFromCell(ChoiceCell cell) {
+
+ List<String> vrCommands;
+ if (cell.getVoiceCommands() == null) {
+ vrCommands = isVROptional ? null : Collections.singletonList(String.valueOf(cell.getChoiceId()));
+ } else {
+ vrCommands = cell.getVoiceCommands();
+ }
+
+ String menuName = shouldSendChoiceText() ? cell.getUniqueText() : null;
+
+ if (menuName == null) {
+ DebugTool.logError(TAG, "Could not convert Choice Cell to CreateInteractionChoiceSet. It will not be shown. Cell: " + cell.toString());
+ return null;
+ }
+
+ String secondaryText = shouldSendChoiceSecondaryText() ? cell.getSecondaryText() : null;
+ String tertiaryText = shouldSendChoiceTertiaryText() ? cell.getTertiaryText() : null;
+
+ Image image = shouldSendChoicePrimaryImage() && cell.getArtwork() != null ? cell.getArtwork().getImageRPC() : null;
+ Image secondaryImage = shouldSendChoiceSecondaryImage() && cell.getSecondaryArtwork() != null ? cell.getSecondaryArtwork().getImageRPC() : null;
+
+ Choice choice = new Choice(cell.getChoiceId(), menuName);
+ choice.setVrCommands(vrCommands);
+ choice.setSecondaryText(secondaryText);
+ choice.setTertiaryText(tertiaryText);
+ choice.setIgnoreAddingVRItems(true);
+
+ if (fileManager.get() != null) {
+ if (image != null && (cell.getArtwork().isStaticIcon() || fileManager.get().hasUploadedFile(cell.getArtwork()))) {
+ choice.setImage(image);
+ }
+ if (secondaryImage != null && (cell.getSecondaryArtwork().isStaticIcon() || fileManager.get().hasUploadedFile(cell.getSecondaryArtwork()))) {
+ choice.setSecondaryImage(secondaryImage);
+ }
+ }
+
+ return new CreateInteractionChoiceSet(choice.getChoiceID(), Collections.singletonList(choice));
+ }
+
+ boolean shouldSendChoiceText() {
+ if (this.displayName != null && this.displayName.equals(DisplayType.GEN3_8_INCH.toString())) {
+ return true;
+ }
+ return defaultMainWindowCapability == null || hasTextFieldOfName(defaultMainWindowCapability, TextFieldName.menuName);
+ }
+
+ boolean shouldSendChoiceSecondaryText() {
+ return defaultMainWindowCapability == null || hasTextFieldOfName(defaultMainWindowCapability, TextFieldName.secondaryText);
+ }
+
+ boolean shouldSendChoiceTertiaryText() {
+ return defaultMainWindowCapability == null || hasTextFieldOfName(defaultMainWindowCapability, TextFieldName.tertiaryText);
+ }
+
+ boolean shouldSendChoicePrimaryImage() {
+ return defaultMainWindowCapability == null || hasImageFieldOfName(defaultMainWindowCapability, ImageFieldName.choiceImage);
+ }
+
+ boolean shouldSendChoiceSecondaryImage() {
+ return defaultMainWindowCapability == null || hasImageFieldOfName(defaultMainWindowCapability, ImageFieldName.choiceSecondaryImage);
+ }
+
+ // SDL Notifications
+ private void addListeners() {
+
+ keyboardRPCListener = new OnRPCNotificationListener() {
+ @Override
+ public void onNotified(RPCNotification notification) {
+ if (getState() == Task.CANCELED) {
+ finishOperation(false);
+ return;
+ }
+
+ if (keyboardListener == null) {
+ DebugTool.logError(TAG, "Received Keyboard Input But Listener is null");
+ return;
+ }
+
+ OnKeyboardInput onKeyboard = (OnKeyboardInput) notification;
+ keyboardListener.onKeyboardDidSendEvent(onKeyboard.getEvent(), onKeyboard.getData());
+
+ if (onKeyboard.getEvent().equals(KeyboardEvent.ENTRY_VOICE) || onKeyboard.getEvent().equals(KeyboardEvent.ENTRY_SUBMITTED)) {
+ // Submit Voice or Text
+ keyboardListener.onUserDidSubmitInput(onKeyboard.getData(), onKeyboard.getEvent());
+ } else if (onKeyboard.getEvent().equals(KeyboardEvent.KEYPRESS)) {
+ // Notify of Keypress
+ keyboardListener.updateAutocompleteWithInput(onKeyboard.getData(), new KeyboardAutocompleteCompletionListener() {
+ @Override
+ public void onUpdatedAutoCompleteList(List<String> updatedAutoCompleteList) {
+ keyboardProperties.setAutoCompleteList(updatedAutoCompleteList != null ? updatedAutoCompleteList : new ArrayList<String>());
+ keyboardProperties.setAutoCompleteText(updatedAutoCompleteList != null && !updatedAutoCompleteList.isEmpty() ? updatedAutoCompleteList.get(0) : null);
+ updateKeyboardProperties(null);
+ }
+ });
+
+ keyboardListener.updateCharacterSetWithInput(onKeyboard.getData(), new KeyboardCharacterSetCompletionListener() {
+ @Override
+ public void onUpdatedCharacterSet(List<String> updatedCharacterSet) {
+ keyboardProperties.setLimitedCharacterList(updatedCharacterSet);
+ updateKeyboardProperties(null);
+ }
+ });
+ } else if (onKeyboard.getEvent().equals(KeyboardEvent.ENTRY_ABORTED) || onKeyboard.getEvent().equals(KeyboardEvent.ENTRY_CANCELLED)) {
+ // Notify of abort / Cancellation
+ keyboardListener.onKeyboardDidAbortWithReason(onKeyboard.getEvent());
+ } else if (onKeyboard.getEvent().equals(KeyboardEvent.INPUT_KEY_MASK_ENABLED) || onKeyboard.getEvent().equals(KeyboardEvent.INPUT_KEY_MASK_DISABLED)) {
+ keyboardListener.onKeyboardDidUpdateInputMask(onKeyboard.getEvent());
+ }
+ }
+ };
+
+ if (internalInterface.get() != null) {
+ internalInterface.get().addOnRPCNotificationListener(FunctionID.ON_KEYBOARD_INPUT, keyboardRPCListener);
+ } else {
+ DebugTool.logError(TAG, "Present Choice Set Keyboard Listener Not Added");
+ }
+ }
+
+ public void setLoadedCells(HashSet<ChoiceCell> loadedCells) {
+ this.loadedCells = loadedCells;
+ }
+
+ HashSet<SdlArtwork> artworksToUpload() {
+ HashSet<SdlArtwork> artworksToUpload = new HashSet<>();
+ for (ChoiceCell cell : cellsToUpload) {
+ if (shouldSendChoicePrimaryImage() && fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getArtwork())) {
+ artworksToUpload.add(cell.getArtwork());
+ }
+ if (shouldSendChoiceSecondaryImage() && fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getSecondaryArtwork())) {
+ artworksToUpload.add(cell.getSecondaryArtwork());
+ }
+ }
+ return artworksToUpload;
+ }
+
+ LayoutMode getLayoutMode() {
+ switch (choiceSet.getLayout()) {
+ case CHOICE_SET_LAYOUT_LIST:
+ return keyboardListener != null ? LayoutMode.LIST_WITH_SEARCH : LayoutMode.LIST_ONLY;
+ case CHOICE_SET_LAYOUT_TILES:
+ return keyboardListener != null ? LayoutMode.ICON_WITH_SEARCH : LayoutMode.ICON_ONLY;
+ }
+ return LayoutMode.LIST_ONLY; // default
+ }
+
+ private List<Integer> getChoiceIds() {
+
+ List<Integer> choiceIds = new ArrayList<>(choiceSet.getChoices().size());
+ for (ChoiceCell cell : choiceSet.getChoices()) {
+ choiceIds.add(cell.getChoiceId());
+ }
+ return choiceIds;
+ }
+
+ void finishOperation(boolean success) {
+ this.currentState = SDLPreloadPresentChoicesOperationState.FINISHING;
+
+ if (this.completionListener != null) {
+ this.completionListener.onComplete(success, loadedCells);
+ }
+
+ if (this.choiceSet == null || this.choiceSet.getChoiceSetSelectionListener() == null) {
+ DebugTool.logWarning(TAG, "");
+ }
+
+
+ if (updatedKeyboardProperties) {
+ // We need to reset the keyboard properties
+ SetGlobalProperties setGlobalProperties = new SetGlobalProperties();
+ setGlobalProperties.setKeyboardProperties(originalKeyboardProperties);
+ setGlobalProperties.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ if (response.getSuccess()) {
+ updatedKeyboardProperties = false;
+ DebugTool.logInfo(TAG, "Successfully reset choice keyboard properties to original config");
+ } else {
+ DebugTool.logError(TAG, "Failed to reset choice keyboard properties to original config " + response.getResultCode() + ", " + response.getInfo());
+ }
+ onFinished();
+ }
+ });
+
+ if (internalInterface.get() != null) {
+ internalInterface.get().sendRPC(setGlobalProperties);
+ internalInterface.get().removeOnRPCNotificationListener(FunctionID.ON_KEYBOARD_INPUT, keyboardRPCListener);
+ } else {
+ DebugTool.logError(TAG, "Internal Interface null when finishing choice keyboard reset");
+ }
+ } else {
+ onFinished();
+ }
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java
deleted file mode 100644
index b5d3945d1..000000000
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java
+++ /dev/null
@@ -1,374 +0,0 @@
-/*
- * Copyright (c) 2019 Livio, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * Neither the name of the Livio Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * Created by brettywhite on 6/12/19 1:52 PM
- *
- */
-
-package com.smartdevicelink.managers.screen.choiceset;
-
-import com.livio.taskmaster.Task;
-import com.smartdevicelink.managers.CompletionListener;
-import com.smartdevicelink.managers.ISdl;
-import com.smartdevicelink.protocol.enums.FunctionID;
-import com.smartdevicelink.proxy.RPCNotification;
-import com.smartdevicelink.proxy.RPCResponse;
-import com.smartdevicelink.proxy.rpc.CancelInteraction;
-import com.smartdevicelink.proxy.rpc.KeyboardProperties;
-import com.smartdevicelink.proxy.rpc.OnKeyboardInput;
-import com.smartdevicelink.proxy.rpc.PerformInteraction;
-import com.smartdevicelink.proxy.rpc.PerformInteractionResponse;
-import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
-import com.smartdevicelink.proxy.rpc.SetGlobalProperties;
-import com.smartdevicelink.proxy.rpc.enums.InteractionMode;
-import com.smartdevicelink.proxy.rpc.enums.KeyboardEvent;
-import com.smartdevicelink.proxy.rpc.enums.LayoutMode;
-import com.smartdevicelink.proxy.rpc.enums.Result;
-import com.smartdevicelink.proxy.rpc.enums.TriggerSource;
-import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
-import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
-import com.smartdevicelink.util.DebugTool;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-
-class PresentChoiceSetOperation extends Task {
- private static final String TAG = "PresentChoiceSetOperation";
- private final WeakReference<ISdl> internalInterface;
- private final ChoiceSet choiceSet;
- private final Integer cancelID;
- private final InteractionMode presentationMode;
- private final KeyboardProperties originalKeyboardProperties;
- private KeyboardProperties keyboardProperties;
- private ChoiceCell selectedCell;
- private TriggerSource selectedTriggerSource;
- private boolean updatedKeyboardProperties;
- private OnRPCNotificationListener keyboardRPCListener;
- private final ChoiceSetSelectionListener choiceSetSelectionListener;
- Integer selectedCellRow;
- KeyboardListener keyboardListener;
- final SdlMsgVersion sdlMsgVersion;
-
- PresentChoiceSetOperation(ISdl internalInterface, ChoiceSet choiceSet, InteractionMode mode,
- KeyboardProperties originalKeyboardProperties, KeyboardListener keyboardListener, ChoiceSetSelectionListener choiceSetSelectionListener, Integer cancelID) {
- super("PresentChoiceSetOperation");
- this.internalInterface = new WeakReference<>(internalInterface);
- this.keyboardListener = keyboardListener;
- this.choiceSet = choiceSet;
- this.choiceSet.canceledListener = new ChoiceSetCanceledListener() {
- @Override
- public void onChoiceSetCanceled() {
- cancelInteraction();
- }
- };
- this.presentationMode = mode;
- this.cancelID = cancelID;
- this.originalKeyboardProperties = originalKeyboardProperties;
- this.keyboardProperties = originalKeyboardProperties;
- this.selectedCellRow = null;
- this.choiceSetSelectionListener = choiceSetSelectionListener;
- this.sdlMsgVersion = internalInterface.getSdlMsgVersion();
- }
-
- @Override
- public void onExecute() {
- DebugTool.logInfo(TAG, "Choice Operation: Executing present choice set operation");
- addListeners();
- start();
- }
-
- private void start() {
- if (getState() == Task.CANCELED) {
- finishOperation();
- return;
- }
-
- // Check if we're using a keyboard (searchable) choice set and setup keyboard properties if we need to
- if (keyboardListener != null && choiceSet.getCustomKeyboardConfiguration() != null) {
- keyboardProperties = choiceSet.getCustomKeyboardConfiguration();
- updatedKeyboardProperties = true;
- }
-
- updateKeyboardProperties(new CompletionListener() {
- @Override
- public void onComplete(boolean success) {
- if (getState() == Task.CANCELED) {
- finishOperation();
- return;
- }
- presentChoiceSet();
- }
- });
- }
-
- // SENDING REQUESTS
-
- private void updateKeyboardProperties(final CompletionListener listener) {
- if (keyboardProperties == null) {
- if (listener != null) {
- listener.onComplete(false);
- }
- return;
- }
- SetGlobalProperties setGlobalProperties = new SetGlobalProperties();
- setGlobalProperties.setKeyboardProperties(keyboardProperties);
- setGlobalProperties.setOnRPCResponseListener(new OnRPCResponseListener() {
- @Override
- public void onResponse(int correlationId, RPCResponse response) {
-
- if (!response.getSuccess()) {
- if (listener != null) {
- listener.onComplete(false);
- }
- DebugTool.logError(TAG, "Error Setting keyboard properties in present choice set operation");
- return;
- }
-
- updatedKeyboardProperties = true;
-
- if (listener != null) {
- listener.onComplete(true);
- }
- DebugTool.logInfo(TAG, "Success Setting keyboard properties in present choice set operation");
- }
- });
- if (internalInterface.get() != null) {
- internalInterface.get().sendRPC(setGlobalProperties);
- } else {
- DebugTool.logError(TAG, "Internal interface null - present choice set op - choice");
- }
- }
-
- private void presentChoiceSet() {
- PerformInteraction pi = getPerformInteraction();
- pi.setOnRPCResponseListener(new OnRPCResponseListener() {
- @Override
- public void onResponse(int correlationId, RPCResponse response) {
- if (!response.getSuccess()) {
- DebugTool.logError(TAG, "Presenting Choice set failed: " + response.getInfo());
-
- if (choiceSetSelectionListener != null) {
- choiceSetSelectionListener.onError(response.getInfo());
- }
- finishOperation();
- return;
- }
-
- PerformInteractionResponse performInteractionResponse = (PerformInteractionResponse) response;
- setSelectedCellWithId(performInteractionResponse.getChoiceID());
- selectedTriggerSource = performInteractionResponse.getTriggerSource();
-
- if (choiceSetSelectionListener != null && selectedCell != null && selectedTriggerSource != null && selectedCellRow != null) {
- choiceSetSelectionListener.onChoiceSelected(selectedCell, selectedTriggerSource, selectedCellRow);
- }
-
- finishOperation();
- }
- });
- if (internalInterface.get() != null) {
- internalInterface.get().sendRPC(pi);
- } else {
- DebugTool.logError(TAG, "Internal Interface null when presenting choice set in operation");
- }
- }
-
- void finishOperation() {
- if (updatedKeyboardProperties) {
- // We need to reset the keyboard properties
- SetGlobalProperties setGlobalProperties = new SetGlobalProperties();
- setGlobalProperties.setKeyboardProperties(originalKeyboardProperties);
- setGlobalProperties.setOnRPCResponseListener(new OnRPCResponseListener() {
- @Override
- public void onResponse(int correlationId, RPCResponse response) {
- if (response.getSuccess()) {
- updatedKeyboardProperties = false;
- DebugTool.logInfo(TAG, "Successfully reset choice keyboard properties to original config");
- } else {
- DebugTool.logError(TAG, "Failed to reset choice keyboard properties to original config " + response.getResultCode() + ", " + response.getInfo());
- }
- PresentChoiceSetOperation.super.onFinished();
- }
- });
-
- if (internalInterface.get() != null) {
- internalInterface.get().sendRPC(setGlobalProperties);
- internalInterface.get().removeOnRPCNotificationListener(FunctionID.ON_KEYBOARD_INPUT, keyboardRPCListener);
- } else {
- DebugTool.logError(TAG, "Internal Interface null when finishing choice keyboard reset");
- }
- } else {
- PresentChoiceSetOperation.super.onFinished();
- }
- }
-
- /*
- * Cancels the choice set. If the choice set has not yet been sent to Core, it will not be sent. If the choice set is already presented on Core, the choice set will be dismissed using the `CancelInteraction` RPC.
- */
- private void cancelInteraction() {
- if ((getState() == Task.FINISHED)) {
- DebugTool.logInfo(TAG, "This operation has already finished so it can not be canceled.");
- return;
- } else if (getState() == Task.CANCELED) {
- DebugTool.logInfo(TAG, "This operation has already been canceled. It will be finished at some point during the operation.");
- return;
- } else if ((getState() == Task.IN_PROGRESS)) {
- if (sdlMsgVersion.getMajorVersion() < 6) {
- DebugTool.logWarning(TAG, "Canceling a presented choice set is not supported on this head unit");
- return;
- }
-
- DebugTool.logInfo(TAG, "Canceling the presented choice set interaction.");
-
- CancelInteraction cancelInteraction = new CancelInteraction(FunctionID.PERFORM_INTERACTION.getId(), cancelID);
- cancelInteraction.setOnRPCResponseListener(new OnRPCResponseListener() {
- @Override
- public void onResponse(int correlationId, RPCResponse response) {
- DebugTool.logInfo(TAG, "Canceled the presented choice set " + ((response.getResultCode() == Result.SUCCESS) ? "successfully" : "unsuccessfully"));
- }
- });
-
- if (internalInterface.get() != null) {
- internalInterface.get().sendRPC(cancelInteraction);
- } else {
- DebugTool.logError(TAG, "Internal interface null - could not send cancel interaction for choice set");
- }
- } else {
- DebugTool.logInfo(TAG, "Canceling a choice set that has not yet been sent to Core");
- this.cancelTask();
- }
- }
-
- // GETTERS
-
- PerformInteraction getPerformInteraction() {
- PerformInteraction pi = new PerformInteraction(choiceSet.getTitle(), presentationMode, getChoiceIds());
- pi.setInitialPrompt(choiceSet.getInitialPrompt());
- pi.setHelpPrompt(choiceSet.getHelpPrompt());
- pi.setTimeoutPrompt(choiceSet.getTimeoutPrompt());
- pi.setVrHelp(choiceSet.getVrHelpList());
- pi.setTimeout(choiceSet.getTimeout() * 1000);
- pi.setInteractionLayout(getLayoutMode());
- pi.setCancelID(cancelID);
- return pi;
- }
-
- LayoutMode getLayoutMode() {
- switch (choiceSet.getLayout()) {
- case CHOICE_SET_LAYOUT_LIST:
- return keyboardListener != null ? LayoutMode.LIST_WITH_SEARCH : LayoutMode.LIST_ONLY;
- case CHOICE_SET_LAYOUT_TILES:
- return keyboardListener != null ? LayoutMode.ICON_WITH_SEARCH : LayoutMode.ICON_ONLY;
- }
- return LayoutMode.LIST_ONLY; // default
- }
-
- private List<Integer> getChoiceIds() {
-
- List<Integer> choiceIds = new ArrayList<>(choiceSet.getChoices().size());
- for (ChoiceCell cell : choiceSet.getChoices()) {
- choiceIds.add(cell.getChoiceId());
- }
- return choiceIds;
- }
-
- // HELPERS
-
- void setSelectedCellWithId(Integer cellId) {
- if (choiceSet.getChoices() != null && cellId != null) {
- List<ChoiceCell> cells = choiceSet.getChoices();
- for (int i = 0; i < cells.size(); i++) {
- if (cells.get(i).getChoiceId() == cellId) {
- selectedCell = cells.get(i);
- selectedCellRow = i;
- return;
- }
- }
- }
- }
-
- // LISTENERS
-
- private void addListeners() {
-
- keyboardRPCListener = new OnRPCNotificationListener() {
- @Override
- public void onNotified(RPCNotification notification) {
- if (getState() == Task.CANCELED) {
- finishOperation();
- return;
- }
-
- if (keyboardListener == null) {
- DebugTool.logError(TAG, "Received Keyboard Input But Listener is null");
- return;
- }
-
- OnKeyboardInput onKeyboard = (OnKeyboardInput) notification;
- keyboardListener.onKeyboardDidSendEvent(onKeyboard.getEvent(), onKeyboard.getData());
-
- if (onKeyboard.getEvent().equals(KeyboardEvent.ENTRY_VOICE) || onKeyboard.getEvent().equals(KeyboardEvent.ENTRY_SUBMITTED)) {
- // Submit Voice or Text
- keyboardListener.onUserDidSubmitInput(onKeyboard.getData(), onKeyboard.getEvent());
- } else if (onKeyboard.getEvent().equals(KeyboardEvent.KEYPRESS)) {
- // Notify of Keypress
- keyboardListener.updateAutocompleteWithInput(onKeyboard.getData(), new KeyboardAutocompleteCompletionListener() {
- @Override
- public void onUpdatedAutoCompleteList(List<String> updatedAutoCompleteList) {
- keyboardProperties.setAutoCompleteList(updatedAutoCompleteList != null ? updatedAutoCompleteList : new ArrayList<String>());
- keyboardProperties.setAutoCompleteText(updatedAutoCompleteList != null && !updatedAutoCompleteList.isEmpty() ? updatedAutoCompleteList.get(0) : null);
- updateKeyboardProperties(null);
- }
- });
-
- keyboardListener.updateCharacterSetWithInput(onKeyboard.getData(), new KeyboardCharacterSetCompletionListener() {
- @Override
- public void onUpdatedCharacterSet(List<String> updatedCharacterSet) {
- keyboardProperties.setLimitedCharacterList(updatedCharacterSet);
- updateKeyboardProperties(null);
- }
- });
- } else if (onKeyboard.getEvent().equals(KeyboardEvent.ENTRY_ABORTED) || onKeyboard.getEvent().equals(KeyboardEvent.ENTRY_CANCELLED)) {
- // Notify of abort / Cancellation
- keyboardListener.onKeyboardDidAbortWithReason(onKeyboard.getEvent());
- } else if (onKeyboard.getEvent().equals(KeyboardEvent.INPUT_KEY_MASK_ENABLED) || onKeyboard.getEvent().equals(KeyboardEvent.INPUT_KEY_MASK_DISABLED)) {
- keyboardListener.onKeyboardDidUpdateInputMask(onKeyboard.getEvent());
- }
- }
- };
-
- if (internalInterface.get() != null) {
- internalInterface.get().addOnRPCNotificationListener(FunctionID.ON_KEYBOARD_INPUT, keyboardRPCListener);
- } else {
- DebugTool.logError(TAG, "Present Choice Set Keyboard Listener Not Added");
- }
- }
-} \ No newline at end of file
diff --git a/base/src/main/java/com/smartdevicelink/protocol/BaseSdlPacket.java b/base/src/main/java/com/smartdevicelink/protocol/BaseSdlPacket.java
index 9694d3f06..fdbba048d 100644
--- a/base/src/main/java/com/smartdevicelink/protocol/BaseSdlPacket.java
+++ b/base/src/main/java/com/smartdevicelink/protocol/BaseSdlPacket.java
@@ -264,10 +264,15 @@ class BaseSdlPacket {
this.priorityCoefficient = priority;
}
+ @Deprecated
public int getPrioirtyCoefficient() {
return this.priorityCoefficient;
}
+ public int getPriorityCoefficient() {
+ return this.priorityCoefficient;
+ }
+
public void setTransportRecord(TransportRecord transportRecord) {
this.transportRecord = transportRecord;
}
diff --git a/base/src/main/java/com/smartdevicelink/protocol/ProtocolMessage.java b/base/src/main/java/com/smartdevicelink/protocol/ProtocolMessage.java
index 8a1968f36..9e2ab38d0 100644
--- a/base/src/main/java/com/smartdevicelink/protocol/ProtocolMessage.java
+++ b/base/src/main/java/com/smartdevicelink/protocol/ProtocolMessage.java
@@ -182,7 +182,12 @@ public class ProtocolMessage {
this.priorityCoefficient = priority;
}
+ @Deprecated
public int getPrioirtyCoefficient() {
return this.priorityCoefficient;
}
+
+ public int getPriorityCoefficient() {
+ return this.priorityCoefficient;
+ }
} // end-class \ No newline at end of file
diff --git a/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java b/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java
index 14222a548..e80255d1b 100644
--- a/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java
+++ b/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java
@@ -572,11 +572,7 @@ public class SdlProtocolBase {
if (sessionType.eq(SessionType.CONTROL)) {
final byte[] secureData = protocolMsg.getData().clone();
data = new byte[headerSize + secureData.length];
-
- final BinaryFrameHeader binFrameHeader =
- SdlPacketFactory.createBinaryFrameHeader(protocolMsg.getRPCType(), protocolMsg.getFunctionID(), protocolMsg.getCorrID(), 0);
- System.arraycopy(binFrameHeader.assembleHeaderBytes(), 0, data, 0, headerSize);
- System.arraycopy(secureData, 0, data, headerSize, secureData.length);
+ System.arraycopy(secureData, 0, data, 0, secureData.length);
} else if (protocolMsg.getBulkData() != null) {
data = new byte[12 + protocolMsg.getJsonSize() + protocolMsg.getBulkData().length];
sessionType = SessionType.BULK_DATA;
diff --git a/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java b/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java
new file mode 100644
index 000000000..c7bc1326d
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java
@@ -0,0 +1,157 @@
+package com.smartdevicelink.protocol;
+
+import androidx.annotation.RestrictTo;
+
+import com.smartdevicelink.protocol.enums.SecurityQueryErrorCode;
+import com.smartdevicelink.protocol.enums.SecurityQueryID;
+import com.smartdevicelink.protocol.enums.SecurityQueryType;
+import com.smartdevicelink.util.BitConverter;
+import com.smartdevicelink.util.DebugTool;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class SecurityQueryPayload {
+ private static final String TAG = "BinaryQueryHeader";
+
+ private SecurityQueryType _securityQueryType;
+ private SecurityQueryID _securityQueryID;
+ private int _correlationID;
+ private int _jsonSize;
+ private SecurityQueryErrorCode _errorCode;
+
+ private byte[] _jsonData = null;
+ private byte[] _bulkData = null;
+
+ private static final int SECURITY_QUERY_HEADER_SIZE = 12;
+
+ public SecurityQueryPayload() {
+ }
+
+ public static SecurityQueryPayload parseBinaryQueryHeader(byte[] binHeader) {
+ if (binHeader == null || binHeader.length < SECURITY_QUERY_HEADER_SIZE) {
+ DebugTool.logError(TAG, "Security Payload error: not enough data to form a Security Query Header. Data length: " + (binHeader != null ? binHeader.length : "null"));
+ return null;
+ }
+
+ SecurityQueryPayload msg = new SecurityQueryPayload();
+
+ //Set QueryType from the first 8 bits
+ byte QUERY_Type = (byte) (binHeader[0]);
+ msg.setQueryType(SecurityQueryType.valueOf(QUERY_Type));
+
+ //Set queryID from the last 24 bits of the first 32 bits
+ byte[] _queryID = new byte[3];
+ System.arraycopy(binHeader, 1, _queryID, 0, 3);
+ msg.setQueryID(SecurityQueryID.valueOf(_queryID));
+
+ //set correlationID from the 32 bits after the first 32 bits
+ int corrID = BitConverter.intFromByteArray(binHeader, 4);
+ msg.setCorrelationID(corrID);
+
+ //set jsonSize from the last 32 bits after the first 64 bits
+ int _jsonSize = BitConverter.intFromByteArray(binHeader, 8);
+ msg.setJsonSize(_jsonSize);
+
+ //If we get an error message we want the error code from the last 8 bits
+ if (msg.getQueryType() == SecurityQueryType.NOTIFICATION && msg.getQueryID() == SecurityQueryID.SEND_INTERNAL_ERROR) {
+ msg.setErrorCode(SecurityQueryErrorCode.valueOf(binHeader[binHeader.length - 1]));
+ }
+
+ try {
+ //Get the JsonData after the header (after 96 bits) based on the jsonData size
+ if (_jsonSize > 0 && _jsonSize <= (binHeader.length - SECURITY_QUERY_HEADER_SIZE)) {
+ byte[] _jsonData = new byte[_jsonSize];
+ System.arraycopy(binHeader, SECURITY_QUERY_HEADER_SIZE, _jsonData, 0, _jsonSize);
+ msg.setJsonData(_jsonData);
+ }
+
+ //Get the binaryData after the header (after 96 bits) and the jsonData size
+ if (binHeader.length - _jsonSize - SECURITY_QUERY_HEADER_SIZE > 0) {
+ byte[] _bulkData;
+ if (msg.getQueryType() == SecurityQueryType.NOTIFICATION && msg.getQueryID() == SecurityQueryID.SEND_INTERNAL_ERROR) {
+ _bulkData = new byte[binHeader.length - _jsonSize - SECURITY_QUERY_HEADER_SIZE - 1];
+ } else {
+ _bulkData = new byte[binHeader.length - _jsonSize - SECURITY_QUERY_HEADER_SIZE];
+ }
+ System.arraycopy(binHeader, SECURITY_QUERY_HEADER_SIZE + _jsonSize, _bulkData, 0, _bulkData.length);
+ msg.setBulkData(_bulkData);
+ }
+
+ } catch (OutOfMemoryError | ArrayIndexOutOfBoundsException e) {
+ DebugTool.logError(TAG, "Unable to process data to form header");
+ return null;
+ }
+
+ return msg;
+ }
+
+ public byte[] assembleHeaderBytes() {
+ // From the properties, create a data buffer
+ // Query Type - first 8 bits
+ // Query ID - next 24 bits
+ // Sequence Number - next 32 bits
+ // JSON size - next 32 bits
+ byte[] ret = new byte[SECURITY_QUERY_HEADER_SIZE];
+ ret[0] = _securityQueryType.getValue();
+ System.arraycopy(_securityQueryID.getValue(), 0, ret, 1, 3);
+ System.arraycopy(BitConverter.intToByteArray(_correlationID), 0, ret, 4, 4);
+ System.arraycopy(BitConverter.intToByteArray(_jsonSize), 0, ret, 8, 4);
+ return ret;
+ }
+
+ public SecurityQueryType getQueryType() {
+ return _securityQueryType;
+ }
+
+ public void setQueryType(SecurityQueryType _securityQueryType) {
+ this._securityQueryType = _securityQueryType;
+ }
+
+ public SecurityQueryID getQueryID() {
+ return _securityQueryID;
+ }
+
+ public void setQueryID(SecurityQueryID _securityQueryID) {
+ this._securityQueryID = _securityQueryID;
+ }
+
+ public int getCorrelationID() {
+ return _correlationID;
+ }
+
+ public void setCorrelationID(int _correlationID) {
+ this._correlationID = _correlationID;
+ }
+
+ public int getJsonSize() {
+ return _jsonSize;
+ }
+
+ public void setJsonSize(int _jsonSize) {
+ this._jsonSize = _jsonSize;
+ }
+
+ public SecurityQueryErrorCode getErrorCode() {
+ return _errorCode;
+ }
+
+ public void setErrorCode(SecurityQueryErrorCode _errorCode) {
+ this._errorCode = _errorCode;
+ }
+
+ public byte[] getJsonData() {
+ return _jsonData;
+ }
+
+ public void setJsonData(byte[] _jsonData) {
+ this._jsonData = new byte[this._jsonSize];
+ System.arraycopy(_jsonData, 0, this._jsonData, 0, _jsonSize);
+ }
+
+ public byte[] getBulkData() {
+ return _bulkData;
+ }
+
+ public void setBulkData(byte[] _bulkData) {
+ this._bulkData = _bulkData;
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryErrorCode.java b/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryErrorCode.java
new file mode 100644
index 000000000..5601271d3
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryErrorCode.java
@@ -0,0 +1,61 @@
+package com.smartdevicelink.protocol.enums;
+
+import androidx.annotation.RestrictTo;
+
+import com.smartdevicelink.util.ByteEnumer;
+
+import java.util.Vector;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class SecurityQueryErrorCode extends ByteEnumer {
+
+ private static final Vector<SecurityQueryErrorCode> theList = new Vector<>();
+
+ public static Vector<SecurityQueryErrorCode> getList() {
+ return theList;
+ }
+
+ protected SecurityQueryErrorCode(byte value, String name) {
+ super(value, name);
+ }
+
+ public final static SecurityQueryErrorCode ERROR_SUCCESS = new SecurityQueryErrorCode((byte) 0x00, "ERROR_SUCCESS");
+ public final static SecurityQueryErrorCode ERROR_INVALID_QUERY_SIZE = new SecurityQueryErrorCode((byte) 0x01, "ERROR_INVALID_QUERY_SIZE");
+ public final static SecurityQueryErrorCode ERROR_INVALID_QUERY_ID = new SecurityQueryErrorCode((byte) 0x02, "ERROR_INVALID_QUERY_ID");
+ public final static SecurityQueryErrorCode ERROR_NOT_SUPPORTED = new SecurityQueryErrorCode((byte) 0x03, "ERROR_NOT_SUPPORTED");
+ public final static SecurityQueryErrorCode ERROR_SERVICE_ALREADY_PROTECTED = new SecurityQueryErrorCode((byte) 0x04, "ERROR_SERVICE_ALREADY_PROTECTED");
+ public final static SecurityQueryErrorCode ERROR_SERVICE_NOT_PROTECTED = new SecurityQueryErrorCode((byte) 0x05, "ERROR_SERVICE_NOT_PROTECTED");
+ public final static SecurityQueryErrorCode ERROR_DECRYPTION_FAILED = new SecurityQueryErrorCode((byte) 0x06, "ERROR_DECRYPTION_FAILED");
+ public final static SecurityQueryErrorCode ERROR_ENCRYPTION_FAILED = new SecurityQueryErrorCode((byte) 0x07, "ERROR_ENCRYPTION_FAILED");
+ public final static SecurityQueryErrorCode ERROR_SSL_INVALID_DATA = new SecurityQueryErrorCode((byte) 0x08, "ERROR_SSL_INVALID_DATA");
+ public final static SecurityQueryErrorCode ERROR_HANDSHAKE_FAILED = new SecurityQueryErrorCode((byte) 0x09, "ERROR_HANDSHAKE_FAILED");
+ public final static SecurityQueryErrorCode INVALID_CERT = new SecurityQueryErrorCode((byte) 0x0A, "INVALID_CERT");
+ public final static SecurityQueryErrorCode EXPIRED_CERT = new SecurityQueryErrorCode((byte) 0x0B, "EXPIRED_CERT");
+ public final static SecurityQueryErrorCode ERROR_INTERNAL = new SecurityQueryErrorCode((byte) 0xFF, "ERROR_INTERNAL");
+ public final static SecurityQueryErrorCode ERROR_UNKNOWN_INTERNAL_ERROR = new SecurityQueryErrorCode((byte) 0xFE, "ERROR_UNKNOWN_INTERNAL_ERROR");
+
+ static {
+ theList.addElement(ERROR_SUCCESS);
+ theList.addElement(ERROR_INVALID_QUERY_SIZE);
+ theList.addElement(ERROR_INVALID_QUERY_ID);
+ theList.addElement(ERROR_NOT_SUPPORTED);
+ theList.addElement(ERROR_SERVICE_ALREADY_PROTECTED);
+ theList.addElement(ERROR_SERVICE_NOT_PROTECTED);
+ theList.addElement(ERROR_DECRYPTION_FAILED);
+ theList.addElement(ERROR_ENCRYPTION_FAILED);
+ theList.addElement(ERROR_SSL_INVALID_DATA);
+ theList.addElement(ERROR_HANDSHAKE_FAILED);
+ theList.addElement(INVALID_CERT);
+ theList.addElement(EXPIRED_CERT);
+ theList.addElement(ERROR_INTERNAL);
+ theList.addElement(ERROR_UNKNOWN_INTERNAL_ERROR);
+ }
+
+ public static SecurityQueryErrorCode valueOf(byte passedByte) {
+ return (SecurityQueryErrorCode) get(theList, passedByte);
+ }
+
+ public static SecurityQueryErrorCode[] values() {
+ return theList.toArray(new SecurityQueryErrorCode[theList.size()]);
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryID.java b/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryID.java
new file mode 100644
index 000000000..c1b799ec9
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryID.java
@@ -0,0 +1,105 @@
+package com.smartdevicelink.protocol.enums;
+
+import androidx.annotation.RestrictTo;
+
+import com.smartdevicelink.util.BitConverter;
+
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.Objects;
+import java.util.Vector;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class SecurityQueryID {
+
+ private static final Vector<SecurityQueryID> theList = new Vector<>();
+
+ public static Vector<SecurityQueryID> getList() {
+ return theList;
+ }
+
+ private static final byte[] sendHandshakeDataByteArray = {(byte) 0x00, (byte) 0x00, (byte) 0x01};
+ private static final byte[] sendInternalErrorByteArray = {(byte) 0x00, (byte) 0x00, (byte) 0x02};
+ private static final byte[] invalidQueryIdByteArray = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
+ public final static SecurityQueryID SEND_HANDSHAKE_DATA = new SecurityQueryID(sendHandshakeDataByteArray, "SEND_HANDSHAKE_DATA");
+ public final static SecurityQueryID SEND_INTERNAL_ERROR = new SecurityQueryID(sendInternalErrorByteArray, "SEND_INTERNAL_ERROR");
+ public final static SecurityQueryID INVALID_QUERY_ID = new SecurityQueryID(invalidQueryIdByteArray, "INVALID_QUERY_ID");
+
+ static {
+ theList.addElement(SEND_HANDSHAKE_DATA);
+ theList.addElement(SEND_INTERNAL_ERROR);
+ theList.addElement(INVALID_QUERY_ID);
+ }
+
+ protected SecurityQueryID(byte[] value, String name) {
+ this.value = value;
+ this.name = name;
+ }
+
+ private final byte[] value;
+ private final String name;
+
+ public byte[] getValue() {
+ return value;
+ }
+
+ public int getIntValue() {
+ byte[] copy = new byte[4];
+ System.arraycopy(value, 0, copy, 1, 3);
+ return BitConverter.intFromByteArray(copy, 0);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean equals(SecurityQueryID other) {
+ return Objects.equals(name, other.getName());
+ }
+
+ public boolean eq(SecurityQueryID other) {
+ return equals(other);
+ }
+
+ public byte[] value() {
+ return value;
+ }
+
+ public static SecurityQueryID get(Vector<?> theList, byte[] value) {
+ Enumeration<?> enumer = theList.elements();
+ while (enumer.hasMoreElements()) {
+ try {
+ SecurityQueryID current = (SecurityQueryID) enumer.nextElement();
+ if (Arrays.equals(current.getValue(), value)) {
+ return current;
+ }
+ } catch (ClassCastException e) {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ public static SecurityQueryID get(Vector<?> theList, String name) {
+ Enumeration<?> enumer = theList.elements();
+ while (enumer.hasMoreElements()) {
+ try {
+ SecurityQueryID current = (SecurityQueryID) enumer.nextElement();
+ if (current.getName().equals(name)) {
+ return current;
+ }
+ } catch (ClassCastException e) {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ public static SecurityQueryID valueOf(byte[] passedByteArray) {
+ return (SecurityQueryID) get(theList, passedByteArray);
+ }
+
+ public static SecurityQueryID[] values() {
+ return theList.toArray(new SecurityQueryID[theList.size()]);
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryType.java b/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryType.java
new file mode 100644
index 000000000..bb021e79c
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryType.java
@@ -0,0 +1,41 @@
+package com.smartdevicelink.protocol.enums;
+
+import androidx.annotation.RestrictTo;
+
+import com.smartdevicelink.util.ByteEnumer;
+
+import java.util.Vector;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class SecurityQueryType extends ByteEnumer {
+
+ private static final Vector<SecurityQueryType> theList = new Vector<>();
+
+ public static Vector<SecurityQueryType> getList() {
+ return theList;
+ }
+
+ protected SecurityQueryType(byte value, String name) {
+ super(value, name);
+ }
+
+ public final static SecurityQueryType REQUEST = new SecurityQueryType((byte) 0x00, "REQUEST");
+ public final static SecurityQueryType RESPONSE = new SecurityQueryType((byte) 0x10, "RESPONSE");
+ public final static SecurityQueryType NOTIFICATION = new SecurityQueryType((byte) 0x20, "NOTIFICATION");
+ public final static SecurityQueryType INVALID_QUERY_TYPE = new SecurityQueryType((byte) 0xFF, "INVALID_QUERY_TYPE");
+
+ static {
+ theList.addElement(REQUEST);
+ theList.addElement(RESPONSE);
+ theList.addElement(NOTIFICATION);
+ theList.addElement(INVALID_QUERY_TYPE);
+ }
+
+ public static SecurityQueryType valueOf(byte passedByte) {
+ return (SecurityQueryType) get(theList, passedByte);
+ }
+
+ public static SecurityQueryType[] values() {
+ return theList.toArray(new SecurityQueryType[theList.size()]);
+ }
+} \ No newline at end of file
diff --git a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java
index 73c6bf0b3..d68e9cd20 100644
--- a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java
+++ b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java
@@ -37,12 +37,15 @@ import androidx.annotation.RestrictTo;
import com.smartdevicelink.exception.SdlException;
import com.smartdevicelink.managers.lifecycle.RpcConverter;
+import com.smartdevicelink.protocol.SecurityQueryPayload;
import com.smartdevicelink.protocol.ISdlProtocol;
import com.smartdevicelink.protocol.ISdlServiceListener;
import com.smartdevicelink.protocol.ProtocolMessage;
import com.smartdevicelink.protocol.SdlPacket;
import com.smartdevicelink.protocol.SdlProtocolBase;
import com.smartdevicelink.protocol.enums.ControlFrameTags;
+import com.smartdevicelink.protocol.enums.SecurityQueryID;
+import com.smartdevicelink.protocol.enums.SecurityQueryType;
import com.smartdevicelink.protocol.enums.SessionType;
import com.smartdevicelink.proxy.RPCMessage;
import com.smartdevicelink.proxy.rpc.VehicleType;
@@ -188,21 +191,73 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ
protected void processControlService(ProtocolMessage msg) {
- if (sdlSecurity == null)
+ if (sdlSecurity == null || msg.getData() == null)
return;
+
+
+ if (msg.getData().length < 12) {
+ DebugTool.logError(TAG, "Security message is malformed, less than 12 bytes. It does not have a security payload header.");
+ }
+ // Check the client's message header for any internal errors
+ // NOTE: Before Core v8.0.0, all these messages will be notifications. In Core v8.0.0 and later, received messages will have the proper query type. Therefore, we cannot do things based only on the query type being request or response.
+ SecurityQueryPayload receivedHeader = SecurityQueryPayload.parseBinaryQueryHeader(msg.getData().clone());
+ if (receivedHeader == null) {
+ DebugTool.logError(TAG, "Module Security Query could not convert to object.");
+ return;
+ }
+
int iLen = msg.getData().length - 12;
byte[] data = new byte[iLen];
System.arraycopy(msg.getData(), 12, data, 0, iLen);
byte[] dataToRead = new byte[4096];
- Integer iNumBytes = sdlSecurity.runHandshake(data, dataToRead);
+ Integer iNumBytes = null;
- if (iNumBytes == null || iNumBytes <= 0)
+ // If the query is of type `Notification` and the id represents a client internal error, we abort the response message and the encryptionManager will not be in state ready.
+ if (receivedHeader.getQueryID() == SecurityQueryID.SEND_INTERNAL_ERROR
+ && receivedHeader.getQueryType() == SecurityQueryType.NOTIFICATION) {
+ if (receivedHeader.getErrorCode() != null) {
+ DebugTool.logError(TAG, "Security Query module internal error: " + receivedHeader.getErrorCode().getName());
+ } else {
+ DebugTool.logError(TAG, "Security Query module error: No information provided");
+ }
return;
+ }
+
+ if (receivedHeader.getQueryID() != SecurityQueryID.SEND_HANDSHAKE_DATA) {
+ DebugTool.logError(TAG, "Security Query module error: Message is not a SEND_HANDSHAKE_DATA REQUEST");
+ return;
+ }
+
+ if (receivedHeader.getQueryType() == SecurityQueryType.RESPONSE) {
+ DebugTool.logError(TAG, "Security Query module error: Message is a response, which is not supported");
+ return;
+ }
+
+ iNumBytes = sdlSecurity.runHandshake(data, dataToRead);
+
+ // Assemble a security query payload header for our response
+ SecurityQueryPayload responseHeader = new SecurityQueryPayload();
+
+ if (iNumBytes == null || iNumBytes <= 0) {
+ DebugTool.logError(TAG, "Internal Error processing control service");
+
+ responseHeader.setQueryID(SecurityQueryID.SEND_INTERNAL_ERROR);
+ responseHeader.setQueryType(SecurityQueryType.NOTIFICATION);
+ responseHeader.setCorrelationID(msg.getCorrID());
+ responseHeader.setJsonSize(0);
+ } else {
+ responseHeader.setQueryID(SecurityQueryID.SEND_HANDSHAKE_DATA);
+ responseHeader.setQueryType(SecurityQueryType.RESPONSE);
+ responseHeader.setCorrelationID(msg.getCorrID());
+ responseHeader.setJsonSize(0);
+ }
+
+ byte[] returnBytes = new byte[iNumBytes + 12];
+ System.arraycopy(responseHeader.assembleHeaderBytes(), 0, returnBytes, 0, 12);
+ System.arraycopy(dataToRead, 0, returnBytes, 12, iNumBytes);
- byte[] returnBytes = new byte[iNumBytes];
- System.arraycopy(dataToRead, 0, returnBytes, 0, iNumBytes);
ProtocolMessage protocolMessage = new ProtocolMessage();
protocolMessage.setSessionType(SessionType.CONTROL);
protocolMessage.setData(returnBytes);