summaryrefslogtreecommitdiff
path: root/android
diff options
context:
space:
mode:
Diffstat (limited to 'android')
-rwxr-xr-xandroid/hello_sdl_android/build.gradle13
-rwxr-xr-xandroid/hello_sdl_android/src/main/AndroidManifest.xml2
-rwxr-xr-xandroid/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/MainActivity.java74
-rwxr-xr-xandroid/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java6
-rw-r--r--android/sdl_android/build.gradle6
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java3
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/PresentAlertOperationTest.java40
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperationTest.java208
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java2
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java13
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/session/SdlSession.java20
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java32
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterStatusProvider.java14
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/transport/USBAccessoryAttachmentActivity.java4
14 files changed, 378 insertions, 59 deletions
diff --git a/android/hello_sdl_android/build.gradle b/android/hello_sdl_android/build.gradle
index 5feeba93f..fa8a08a93 100755
--- a/android/hello_sdl_android/build.gradle
+++ b/android/hello_sdl_android/build.gradle
@@ -1,13 +1,15 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 31
+ compileSdkVersion 33
defaultConfig {
applicationId "com.sdl.hellosdlandroid"
minSdkVersion 16
- targetSdkVersion 31
+ targetSdkVersion 33
versionCode 1
versionName "1.0"
+ buildConfigField 'String', 'APP_TYPE', '"DEFAULT"'
+ buildConfigField 'String', 'REQUIRE_AUDIO_OUTPUT', '"FALSE"'
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
buildTypes {
@@ -42,6 +44,13 @@ android {
buildConfigField 'String', 'TRANSPORT', '"TCP"'
buildConfigField 'String', 'SECURITY', '"OFF"'
}
+ requiresAudioOutput {
+ buildConfigField 'String', 'TRANSPORT', '"MULTI"'
+ buildConfigField 'String', 'SECURITY', '"OFF"'
+ buildConfigField 'String', 'APP_TYPE', '"MEDIA"'
+ buildConfigField 'String', 'REQUIRE_AUDIO_OUTPUT', '"TRUE"'
+
+ }
}
lintOptions {
disable 'GoogleAppIndexingWarning'
diff --git a/android/hello_sdl_android/src/main/AndroidManifest.xml b/android/hello_sdl_android/src/main/AndroidManifest.xml
index 415aa66c2..6b577c5a2 100755
--- a/android/hello_sdl_android/src/main/AndroidManifest.xml
+++ b/android/hello_sdl_android/src/main/AndroidManifest.xml
@@ -6,6 +6,8 @@
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"
tools:targetApi="31"/>
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS"
+ tools:targetApi="33"/>
<uses-permission android:name="android.permission.INTERNET" />
<!-- Required to check if WiFi is enabled -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
diff --git a/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/MainActivity.java b/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/MainActivity.java
index 8497e3b73..44999945f 100755
--- a/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/MainActivity.java
+++ b/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/MainActivity.java
@@ -1,18 +1,17 @@
package com.sdl.hellosdlandroid;
+import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
-
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
-
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
@@ -23,12 +22,18 @@ public class MainActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
-
if (BuildConfig.TRANSPORT.equals("MULTI") || BuildConfig.TRANSPORT.equals("MULTI_HB")) {
- if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !checkPermission()) {
- requestPermission();
- return;
+ String[] permissionsNeeded = permissionsNeeded();
+ if (permissionsNeeded.length > 0) {
+ requestPermission(permissionsNeeded, REQUEST_CODE);
+ for (String permission : permissionsNeeded) {
+ if (Manifest.permission.BLUETOOTH_CONNECT.equals(permission)) {
+ // We need to request BLUETOOTH_CONNECT permission to connect to SDL via Bluetooth
+ return;
+ }
+ }
}
+
//If we are connected to a module we want to start our SdlService
SdlReceiver.queryForConnectedService(this);
} else if (BuildConfig.TRANSPORT.equals("TCP")){
@@ -37,12 +42,39 @@ public class MainActivity extends AppCompatActivity {
}
}
- private boolean checkPermission() {
- return PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(getApplicationContext(), BLUETOOTH_CONNECT);
+ /**
+ * Boolean method that checks API level and check to see if we need to request BLUETOOTH_CONNECT permission
+ * @return false if we need to request BLUETOOTH_CONNECT permission
+ */
+ private boolean hasBTPermission() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? checkPermission(Manifest.permission.BLUETOOTH_CONNECT) : true;
+ }
+
+ /**
+ * Boolean method that checks API level and check to see if we need to request POST_NOTIFICATIONS permission
+ * @return false if we need to request POST_NOTIFICATIONS permission
+ */
+ private boolean hasPNPermission() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ? checkPermission(Manifest.permission.POST_NOTIFICATIONS) : true;
}
- private void requestPermission() {
- ActivityCompat.requestPermissions(this, new String[]{BLUETOOTH_CONNECT}, REQUEST_CODE);
+ private boolean checkPermission(String permission) {
+ return PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(getApplicationContext(), permission);
+ }
+
+ private void requestPermission(String[] permissions, int REQUEST_CODE) {
+ ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE);
+ }
+
+ private @NonNull String[] permissionsNeeded() {
+ ArrayList<String> result = new ArrayList<>();
+ if (!hasBTPermission()) {
+ result.add(Manifest.permission.BLUETOOTH_CONNECT);
+ }
+ if (!hasPNPermission()) {
+ result.add(Manifest.permission.POST_NOTIFICATIONS);
+ }
+ return (result.toArray(new String[result.size()]));
}
@Override
@@ -50,11 +82,21 @@ public class MainActivity extends AppCompatActivity {
switch (requestCode) {
case REQUEST_CODE:
if (grantResults.length > 0) {
-
- boolean btConnectGranted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
-
- if (btConnectGranted) {
- SdlReceiver.queryForConnectedService(this);
+ for (int i = 0; i < grantResults.length; i++) {
+ if (permissions[i].equals(Manifest.permission.BLUETOOTH_CONNECT)) {
+ boolean btConnectGranted =
+ grantResults[i] == PackageManager.PERMISSION_GRANTED;
+ if (btConnectGranted) {
+ SdlReceiver.queryForConnectedService(this);
+ }
+ } else if (permissions[i].equals(Manifest.permission.POST_NOTIFICATIONS)) {
+ boolean postNotificationGranted =
+ grantResults[i] == PackageManager.PERMISSION_GRANTED;
+ if (!postNotificationGranted) {
+ // User denied permission, Notifications for SDL will not appear
+ // on Android 13 devices.
+ }
+ }
}
}
break;
diff --git a/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java b/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java
index 59daf2050..be147af0b 100755
--- a/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java
+++ b/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java
@@ -162,6 +162,9 @@ public class SdlService extends Service {
securityLevel = MultiplexTransportConfig.FLAG_MULTI_SECURITY_OFF;
}
transport = new MultiplexTransportConfig(this, APP_ID, securityLevel);
+ if (BuildConfig.REQUIRE_AUDIO_OUTPUT.equals("TRUE") ) {
+ ((MultiplexTransportConfig)transport).setRequiresAudioSupport(true);
+ }
} else if (BuildConfig.TRANSPORT.equals("TCP")) {
transport = new TCPTransportConfig(TCP_PORT, DEV_MACHINE_IP_ADDRESS, true);
} else if (BuildConfig.TRANSPORT.equals("MULTI_HB")) {
@@ -172,7 +175,8 @@ public class SdlService extends Service {
// The app type to be used
Vector<AppHMIType> appType = new Vector<>();
- appType.add(AppHMIType.DEFAULT);
+ appType.add(AppHMIType.valueForString(BuildConfig.APP_TYPE));
+
// The manager listener helps you know when certain events that pertain to the SDL Manager happen
// Here we will listen for ON_HMI_STATUS and ON_COMMAND notifications
diff --git a/android/sdl_android/build.gradle b/android/sdl_android/build.gradle
index b0072a6fd..c7cce6954 100644
--- a/android/sdl_android/build.gradle
+++ b/android/sdl_android/build.gradle
@@ -1,11 +1,11 @@
apply plugin: 'com.android.library'
android {
- compileSdkVersion 31
+ compileSdkVersion 33
defaultConfig {
minSdkVersion 16
- targetSdkVersion 31
- versionCode 23
+ targetSdkVersion 33
+ versionCode 24
versionName new File(projectDir.path, ('/../../VERSION')).text.trim()
buildConfigField "String", "VERSION_NAME", '\"' + versionName + '\"'
resValue "string", "SDL_LIB_VERSION", '\"' + versionName + '\"'
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java
index 03854d3ee..abe187cc8 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java
@@ -176,8 +176,9 @@ public class SystemCapabilityManagerTests {
convertedCapabilities.setImageFields(defaultMainWindow.getImageFields());
convertedCapabilities.setTemplatesAvailable(defaultMainWindow.getTemplatesAvailable());
convertedCapabilities.setNumCustomPresetsAvailable(defaultMainWindow.getNumCustomPresetsAvailable());
- convertedCapabilities.setMediaClockFormats(new ArrayList<MediaClockFormat>()); // mandatory field but can be empty
+ convertedCapabilities.setMediaClockFormats(TestValues.GENERAL_MEDIACLOCKFORMAT_LIST); // mandatory field but can be empty
convertedCapabilities.setGraphicSupported(defaultMainWindow.getImageTypeSupported().contains(ImageType.DYNAMIC));
+ convertedCapabilities.setScreenParams(TestValues.GENERAL_SCREENPARAMS);
return convertedCapabilities;
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/PresentAlertOperationTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/PresentAlertOperationTest.java
index 5f69010f1..4e105e1f8 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/PresentAlertOperationTest.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/PresentAlertOperationTest.java
@@ -27,6 +27,7 @@ import com.smartdevicelink.proxy.rpc.WindowCapability;
import com.smartdevicelink.proxy.rpc.enums.FileType;
import com.smartdevicelink.proxy.rpc.enums.ImageFieldName;
import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
+import com.smartdevicelink.proxy.rpc.enums.StaticIconName;
import com.smartdevicelink.proxy.rpc.enums.TextFieldName;
import com.smartdevicelink.test.TestValues;
@@ -42,7 +43,6 @@ import java.util.List;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
@@ -167,7 +167,7 @@ public class PresentAlertOperationTest {
builder.setShowWaitIndicator(true);
alertView = builder.build();
- defaultMainWindowCapability = getWindowCapability(3);
+ defaultMainWindowCapability = getWindowCapability(3, true);
speechCapabilities = new ArrayList<SpeechCapabilities>();
speechCapabilities.add(SpeechCapabilities.FILE);
alertCompletionListener = new AlertCompletionListener() {
@@ -186,13 +186,13 @@ public class PresentAlertOperationTest {
// Same response works for uploading artworks as it does for files
when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
- WindowCapability windowCapability = getWindowCapability(1);
+ WindowCapability windowCapability = getWindowCapability(1, true);
PresentAlertOperation presentAlertOperation = new PresentAlertOperation(internalInterface, alertView, windowCapability, speechCapabilities, fileManager, 1, alertCompletionListener, alertSoftButtonClearListener);
Alert alert = presentAlertOperation.alertRpc();
assertEquals(alert.getAlertText1(), alertView.getText() + " - " + alertView.getSecondaryText() + " - " + alertView.getTertiaryText());
- windowCapability = getWindowCapability(2);
+ windowCapability = getWindowCapability(2, true);
presentAlertOperation = new PresentAlertOperation(internalInterface, alertView, windowCapability, speechCapabilities, fileManager, 1, alertCompletionListener, alertSoftButtonClearListener);
alert = presentAlertOperation.alertRpc();
@@ -258,7 +258,23 @@ public class PresentAlertOperationTest {
verify(fileManager, times(1)).uploadArtworks(any(List.class), any(MultipleFileCompletionListener.class));
verify(internalInterface, times(1)).sendRPC(any(Alert.class));
}
+ @Test
+ public void testPresentStaticIcon() {
+ doAnswer(onAlertSuccess).when(internalInterface).sendRPC(any(Alert.class));
+ // Same response works for uploading artworks as it does for files
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
+ when(fileManager.fileNeedsUpload(any(SdlFile.class))).thenReturn(false);
+
+ alertView.setIcon(new SdlArtwork(StaticIconName.LEFT));
+ PresentAlertOperation presentAlertOperationStaticIcon = new PresentAlertOperation(internalInterface, alertView, defaultMainWindowCapability, speechCapabilities, fileManager, 1, alertCompletionListener, alertSoftButtonClearListener);
+
+ // Test Images need to be uploaded, sending text and uploading images
+ presentAlertOperationStaticIcon.onExecute();
+ // Verifies that uploadArtworks gets called only with the fist presentAlertOperation.onExecute call
+ verify(fileManager, times(0)).uploadArtworks(any(List.class), any(MultipleFileCompletionListener.class));
+ verify(internalInterface, times(1)).sendRPC(any(Alert.class));
+ }
@Test
public void testCancelOperation() {
//Cancel right away
@@ -267,7 +283,7 @@ public class PresentAlertOperationTest {
verify(internalInterface, times(0)).sendRPC(any(Alert.class));
}
- private WindowCapability getWindowCapability(int numberOfAlertFields) {
+ private WindowCapability getWindowCapability(int numberOfAlertFields, boolean supportsAlertIcon) {
TextField alertText1 = new TextField();
alertText1.setName(TextFieldName.alertText1);
TextField alertText2 = new TextField();
@@ -294,13 +310,13 @@ public class PresentAlertOperationTest {
WindowCapability windowCapability = new WindowCapability();
windowCapability.setTextFields(returnList);
- ImageField imageField = new ImageField();
- imageField.setName(ImageFieldName.alertIcon);
- List<ImageField> imageFieldList = new ArrayList<>();
- imageFieldList.add(imageField);
- windowCapability.setImageFields(imageFieldList);
-
- windowCapability.setImageFields(imageFieldList);
+ if (supportsAlertIcon) {
+ ImageField imageField = new ImageField();
+ imageField.setName(ImageFieldName.alertIcon);
+ List<ImageField> imageFieldList = new ArrayList<>();
+ imageFieldList.add(imageField);
+ windowCapability.setImageFields(imageFieldList);
+ }
SoftButtonCapabilities softButtonCapabilities = new SoftButtonCapabilities();
softButtonCapabilities.setImageSupported(TestValues.GENERAL_BOOLEAN);
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperationTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperationTest.java
index 4af040863..d2a821e23 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperationTest.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperationTest.java
@@ -52,20 +52,24 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import junit.framework.TestCase;
+
@RunWith(AndroidJUnit4.class)
public class TextAndGraphicUpdateOperationTest {
private TextAndGraphicUpdateOperation textAndGraphicUpdateOperation;
private String textField1, textField2, textField3, textField4, mediaTrackField, title;
- private MetadataType textField1Type, textField2Type, textField3Type, textField4Type;
- private SdlArtwork testArtwork1, testArtwork2, testArtwork3, testArtwork4;
- private TextAlignment textAlignment;
+ private String textField1Fail, textField2Fail, textField3Fail, textField4Fail, mediaTrackFieldFail, titleFail;
+ private MetadataType textField1Type, textField2Type, textField3Type, textField4Type, textFieldFailType;
+ private SdlArtwork testArtwork1, testArtwork2, testArtwork3, testArtwork4, testArtworkFail;
+ private TextAlignment textAlignment, textAlignmentFail;
private WindowCapability defaultMainWindowCapability;
private TextAndGraphicState currentScreenData;
+ private TextAndGraphicState errorTestState, errorTestState2;
private CompletionListener listener;
private TextAndGraphicManager.CurrentScreenDataUpdatedListener currentScreenDataUpdatedListener;
private SdlArtwork blankArtwork;
- private TemplateConfiguration configuration;
+ private TemplateConfiguration configuration, configurationFail, configurationOld;
ISdl internalInterface;
FileManager fileManager;
@@ -188,6 +192,13 @@ public class TextAndGraphicUpdateOperationTest {
mediaTrackField = "dudes";
title = "dudes";
+ textField1Fail = "It is\nbad data";
+ textField2Fail = "Wednesday\nbad data";
+ textField3Fail = "My\nbad data";
+ textField4Fail = "Dudes\nbad data";
+ mediaTrackFieldFail = "dudes\nbad data";
+ titleFail = "dudes\nbad data";
+
blankArtwork = new SdlArtwork();
blankArtwork.setType(FileType.GRAPHIC_PNG);
blankArtwork.setName("blankArtwork");
@@ -197,9 +208,10 @@ public class TextAndGraphicUpdateOperationTest {
textField2Type = MetadataType.MEDIA_TITLE;
textField3Type = MetadataType.MEDIA_TITLE;
textField4Type = MetadataType.MEDIA_TITLE;
-
+ textFieldFailType = MetadataType.valueForString("failType");
textAlignment = TextAlignment.CENTERED;
+ textAlignmentFail = TextAlignment.valueForString("failAlignment");
testArtwork1 = new SdlArtwork();
testArtwork1.setName("testFile1");
@@ -225,9 +237,39 @@ public class TextAndGraphicUpdateOperationTest {
testArtwork4.setUri(uri4);
testArtwork4.setType(FileType.GRAPHIC_PNG);
+ testArtworkFail = new SdlArtwork();
+ testArtworkFail.setName("testFileFail");
+ Uri uriFail = Uri.parse("android.resource://" + mTestContext.getPackageName() + "/no_file");
+ testArtworkFail.setUri(uriFail);
+ testArtworkFail.setType(FileType.GRAPHIC_PNG);
+
configuration = new TemplateConfiguration();
configuration.setTemplate(PredefinedLayout.GRAPHIC_WITH_TEXT.toString());
+ configurationOld = new TemplateConfiguration();
+ configurationOld.setTemplate(PredefinedLayout.TEXT_WITH_GRAPHIC.toString());
+ configurationFail = new TemplateConfiguration();
+ configurationFail.setTemplate("failConfiguration");
+
+ errorTestState = new TextAndGraphicState();
+ errorTestState2 = new TextAndGraphicState();
+
+ errorTestState2.setTextField1(textField1);
+ errorTestState2.setTextField2(textField2);
+ errorTestState2.setTextField3(textField3Fail);
+ errorTestState2.setTextField4(textField4Fail);
+ errorTestState2.setTextField1Type(textFieldFailType);
+ errorTestState2.setTextField2Type(textFieldFailType);
+ errorTestState2.setTextField3Type(textFieldFailType);
+ errorTestState2.setTextField4Type(textFieldFailType);
+ errorTestState2.setMediaTrackTextField(mediaTrackFieldFail);
+ errorTestState2.setTitle(titleFail);
+ errorTestState2.setPrimaryGraphic(testArtworkFail);
+ errorTestState2.setSecondaryGraphic(testArtworkFail);
+ errorTestState2.setTextAlignment(textAlignmentFail);
+ errorTestState2.setTemplateConfiguration(configurationFail);
+
+
currentScreenData = new TextAndGraphicState();
currentScreenData.setTextField1("Old");
currentScreenData.setTextField2("Text");
@@ -236,17 +278,15 @@ public class TextAndGraphicUpdateOperationTest {
currentScreenData.setPrimaryGraphic(testArtwork1);
currentScreenData.setSecondaryGraphic(testArtwork2);
- currentScreenData.setTemplateConfiguration(configuration);
+ currentScreenData.setTemplateConfiguration(configurationOld);
currentScreenDataUpdatedListener = new TextAndGraphicManager.CurrentScreenDataUpdatedListener() {
@Override
public void onUpdate(TextAndGraphicState newState) {
-
}
@Override
- public void onError() {
-
+ public void onError(TextAndGraphicState errorState) {
}
};
@@ -257,7 +297,6 @@ public class TextAndGraphicUpdateOperationTest {
textAndGraphicUpdateOperation = new TextAndGraphicUpdateOperation(internalInterface, fileManager, defaultMainWindowCapability, currentScreenData, textsAndGraphicsState, listener, currentScreenDataUpdatedListener);
}
-
private void setUpCompletionListener() {
listener = new CompletionListener() {
@Override
@@ -1010,11 +1049,158 @@ public class TextAndGraphicUpdateOperationTest {
mediaTrackField, title, testArtwork3, testArtwork4, textAlignment, textField1Type, textField2Type, textField3Type, textField4Type, configuration);
textAndGraphicUpdateOperation = new TextAndGraphicUpdateOperation(internalInterface, fileManager, defaultMainWindowCapability, currentScreenData, textsAndGraphicsState, listener, currentScreenDataUpdatedListener);
textAndGraphicUpdateOperation.onExecute();
- assertEquals(textAndGraphicUpdateOperation.getCurrentScreenData().getTemplateConfiguration().getStore(), configuration.getStore());
// Verifies that uploadArtworks does not get called because a sendShow failed with text and layout change
verify(fileManager, times(0)).uploadArtworks(any(List.class), any(MultipleFileCompletionListener.class));
}
+ @Test
+ public void testOnShowFailBadDataDoesNotUpdateScreen(){
+ doAnswer(onShowFail).when(internalInterface).sendRPC(any(Show.class));
+ doAnswer(onArtworkUploadSuccess).when(fileManager).uploadArtworks(any(List.class), any(MultipleFileCompletionListener.class));
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(4, 0));
+
+ TextAndGraphicState textsAndGraphicsState = new TextAndGraphicState(textField1Fail, textField2Fail, textField3Fail, textField4Fail,
+ mediaTrackFieldFail, titleFail, testArtworkFail, testArtworkFail, textAlignmentFail, textFieldFailType, textFieldFailType, textFieldFailType, textFieldFailType, configurationFail);
+ textAndGraphicUpdateOperation = new TextAndGraphicUpdateOperation(internalInterface, fileManager, defaultMainWindowCapability, currentScreenData, textsAndGraphicsState, listener, currentScreenDataUpdatedListener);
+
+ // Test Images need to be uploaded, sending text and uploading images
+ textAndGraphicUpdateOperation.onExecute();
+
+ // Sending in bad data should result in no updates to the current screen
+ assertEquals("Old", textAndGraphicUpdateOperation.getCurrentScreenData().getTextField1());
+ assertEquals("Text", textAndGraphicUpdateOperation.getCurrentScreenData().getTextField2());
+ assertEquals("Not", textAndGraphicUpdateOperation.getCurrentScreenData().getTextField3());
+ assertEquals("Important", textAndGraphicUpdateOperation.getCurrentScreenData().getTextField4());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getMediaTrackTextField());
+ assertEquals(testArtwork1, textAndGraphicUpdateOperation.getCurrentScreenData().getPrimaryGraphic());
+ assertEquals(testArtwork2, textAndGraphicUpdateOperation.getCurrentScreenData().getSecondaryGraphic());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextAlignment());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextField1Type());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextField2Type());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextField3Type());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextField4Type());
+ assertEquals(configurationOld, textAndGraphicUpdateOperation.getCurrentScreenData().getTemplateConfiguration());
+ }
+
+ @Test
+ public void testUpdateTargetStateWithErrorStateNullDoesNotUpdateCurrentScreen() {
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(4, 0));
+
+ errorTestState.setTextField1(null);
+ errorTestState.setTextField2(null);
+ errorTestState.setTextField3(null);
+ errorTestState.setTextField4(null);
+ errorTestState.setTextField1Type(null);
+ errorTestState.setTextField2Type(null);
+ errorTestState.setTextField3Type(null);
+ errorTestState.setTextField4Type(null);
+ errorTestState.setMediaTrackTextField(null);
+ errorTestState.setTitle(null);
+ errorTestState.setPrimaryGraphic(null);
+ errorTestState.setSecondaryGraphic(null);
+ errorTestState.setTextAlignment(null);
+ errorTestState.setTemplateConfiguration(null);
+
+ textAndGraphicUpdateOperation = new TextAndGraphicUpdateOperation(internalInterface, fileManager, defaultMainWindowCapability, currentScreenData, errorTestState, listener, currentScreenDataUpdatedListener);
+ // Testing updateTargetStateWithErrorState method
+ textAndGraphicUpdateOperation.updateTargetStateWithErrorState(errorTestState);
+ textAndGraphicUpdateOperation.onExecute();
+
+ // Setting fields to null should result in no updates to the current screen
+ assertEquals("Old", textAndGraphicUpdateOperation.getCurrentScreenData().getTextField1());
+ assertEquals("Text", textAndGraphicUpdateOperation.getCurrentScreenData().getTextField2());
+ assertEquals("Not", textAndGraphicUpdateOperation.getCurrentScreenData().getTextField3());
+ assertEquals("Important", textAndGraphicUpdateOperation.getCurrentScreenData().getTextField4());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getMediaTrackTextField());
+ assertEquals(testArtwork1, textAndGraphicUpdateOperation.getCurrentScreenData().getPrimaryGraphic());
+ assertEquals(testArtwork2, textAndGraphicUpdateOperation.getCurrentScreenData().getSecondaryGraphic());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextAlignment());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextField1Type());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextField2Type());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextField3Type());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextField4Type());
+ assertEquals(configurationOld, textAndGraphicUpdateOperation.getCurrentScreenData().getTemplateConfiguration());
+ }
+
+ @Test
+ public void testUpdateTargetStateWithErrorBadDataDoesNotUpdateCurrentScreen() {
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(4, 0));
+
+ errorTestState.setTextField1(textField1Fail);
+ errorTestState.setTextField2(textField2Fail);
+ errorTestState.setTextField3(textField3Fail);
+ errorTestState.setTextField4(textField4Fail);
+ errorTestState.setTextField1Type(textFieldFailType);
+ errorTestState.setTextField2Type(textFieldFailType);
+ errorTestState.setTextField3Type(textFieldFailType);
+ errorTestState.setTextField4Type(textFieldFailType);
+ errorTestState.setMediaTrackTextField(mediaTrackFieldFail);
+ errorTestState.setTitle(titleFail);
+ errorTestState.setPrimaryGraphic(testArtworkFail);
+ errorTestState.setSecondaryGraphic(testArtworkFail);
+ errorTestState.setTextAlignment(textAlignmentFail);
+ errorTestState.setTemplateConfiguration(configurationFail);
+
+ textAndGraphicUpdateOperation = new TextAndGraphicUpdateOperation(internalInterface, fileManager, defaultMainWindowCapability, currentScreenData, errorTestState, listener, currentScreenDataUpdatedListener);
+ // Testing updateTargetStateWithErrorState method
+ textAndGraphicUpdateOperation.updateTargetStateWithErrorState(errorTestState);
+ textAndGraphicUpdateOperation.onExecute();
+
+ // Setting bad data should result in no updates to the current screen
+ assertEquals("Old", textAndGraphicUpdateOperation.getCurrentScreenData().getTextField1());
+ assertEquals("Text", textAndGraphicUpdateOperation.getCurrentScreenData().getTextField2());
+ assertEquals("Not", textAndGraphicUpdateOperation.getCurrentScreenData().getTextField3());
+ assertEquals("Important", textAndGraphicUpdateOperation.getCurrentScreenData().getTextField4());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getMediaTrackTextField());
+ assertEquals(testArtwork1, textAndGraphicUpdateOperation.getCurrentScreenData().getPrimaryGraphic());
+ assertEquals(testArtwork2, textAndGraphicUpdateOperation.getCurrentScreenData().getSecondaryGraphic());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextAlignment());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextField1Type());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextField2Type());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextField3Type());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextField4Type());
+ assertEquals(configurationOld, textAndGraphicUpdateOperation.getCurrentScreenData().getTemplateConfiguration());
+ }
+
+ @Test
+ public void testUpdateTargetStateWithErrorBadDataAndGoodData() {
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(4, 0));
+
+ errorTestState2.setTextField1(textField1Fail);
+ errorTestState2.setTextField2(textField2);
+ errorTestState2.setTextField3(null);
+ errorTestState2.setTextField4(textField4Fail);
+ errorTestState2.setTextField1Type(textFieldFailType);
+ errorTestState2.setTextField2Type(textFieldFailType);
+ errorTestState2.setTextField3Type(textFieldFailType);
+ errorTestState2.setTextField4Type(textFieldFailType);
+ errorTestState2.setMediaTrackTextField(mediaTrackFieldFail);
+ errorTestState2.setTitle(titleFail);
+ errorTestState2.setPrimaryGraphic(testArtworkFail);
+ errorTestState2.setSecondaryGraphic(testArtworkFail);
+ errorTestState2.setTextAlignment(textAlignmentFail);
+ errorTestState2.setTemplateConfiguration(configurationFail);
+
+ textAndGraphicUpdateOperation = new TextAndGraphicUpdateOperation(internalInterface, fileManager, defaultMainWindowCapability, currentScreenData, errorTestState2, listener, currentScreenDataUpdatedListener);
+ // Testing updateTargetStateWithErrorState method
+ textAndGraphicUpdateOperation.updateTargetStateWithErrorState(errorTestState2);
+ textAndGraphicUpdateOperation.onExecute();
+
+ // Setting mix of good and bad data should result in only updates to those fields with good data
+ assertEquals("Old", textAndGraphicUpdateOperation.getCurrentScreenData().getTextField1());
+ assertEquals(errorTestState2.getTextField2(), textAndGraphicUpdateOperation.getCurrentScreenData().getTextField2());
+ assertEquals("Not", textAndGraphicUpdateOperation.getCurrentScreenData().getTextField3());
+ assertEquals("Important", textAndGraphicUpdateOperation.getCurrentScreenData().getTextField4());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getMediaTrackTextField());
+ assertEquals(errorTestState2.getPrimaryGraphic(), textAndGraphicUpdateOperation.getCurrentScreenData().getPrimaryGraphic());
+ assertEquals(errorTestState2.getSecondaryGraphic(), textAndGraphicUpdateOperation.getCurrentScreenData().getSecondaryGraphic());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextAlignment());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextField1Type());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextField2Type());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextField3Type());
+ TestCase.assertNull(textAndGraphicUpdateOperation.getCurrentScreenData().getTextField4Type());
+ assertEquals(configurationOld, textAndGraphicUpdateOperation.getCurrentScreenData().getTemplateConfiguration());
+ }
}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java
index b46745917..bc7dfe3bb 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java
@@ -160,7 +160,7 @@ public class SdlArtwork extends SdlFile implements Cloneable {
public SdlArtwork clone() {
SdlArtwork artwork = (SdlArtwork) super.clone();
if (artwork != null) {
- artwork.imageRPC = artwork.createImageRPC();
+ artwork.imageRPC = null;
return artwork;
}
return null;
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java
index bfe73c19f..ae8ba7b23 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java
@@ -55,6 +55,7 @@ import com.smartdevicelink.transport.enums.TransportType;
import com.smartdevicelink.transport.utl.TransportRecord;
import com.smartdevicelink.util.AndroidTools;
import com.smartdevicelink.util.DebugTool;
+import com.smartdevicelink.util.MediaStreamingStatus;
import java.lang.ref.WeakReference;
import java.util.List;
@@ -80,7 +81,17 @@ public class LifecycleManager extends BaseLifecycleManager {
synchronized (SESSION_LOCK) {
if (_transportConfig != null && _transportConfig.getTransportType().equals(TransportType.MULTIPLEX)) {
- this.session = new SdlSession(sdlSessionListener, (MultiplexTransportConfig) _transportConfig);
+ MultiplexTransportConfig multiplexTransportConfig = (MultiplexTransportConfig) _transportConfig;
+ this.session = new SdlSession(sdlSessionListener, multiplexTransportConfig);
+ if (multiplexTransportConfig.requiresAudioSupport()) {
+ this.session.setMediaStreamingStatusCallback(new MediaStreamingStatus.Callback() {
+ @Override
+ public void onAudioNoLongerAvailable() {
+ clean(true);
+ onClose("Audio output no longer available", null, SdlDisconnectedReason.DEFAULT);
+ }
+ });
+ }
} else if (_transportConfig != null && _transportConfig.getTransportType().equals(TransportType.TCP)) {
this.session = new SdlSession(sdlSessionListener, (TCPTransportConfig) _transportConfig);
} else {
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/session/SdlSession.java b/android/sdl_android/src/main/java/com/smartdevicelink/session/SdlSession.java
index 74701c867..633904bb0 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/session/SdlSession.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/session/SdlSession.java
@@ -62,6 +62,7 @@ public class SdlSession extends BaseSdlSession {
WeakReference<Context> contextWeakReference;
MediaStreamingStatus mediaStreamingStatus;
boolean requiresAudioSupport = false;
+ MediaStreamingStatus.Callback mediaStreamingStatusCallback;
public SdlSession(ISdlSessionListener listener, MultiplexTransportConfig config) {
super(listener, config);
@@ -79,6 +80,17 @@ public class SdlSession extends BaseSdlSession {
this.sessionListener = listener;
}
+ /**
+ * Sets a callback that is triggered when there are no audio output methods available. If this
+ * is set then the caller of this method will be responsible for shutting the session down.
+ *
+ * @param mediaStreamingStatusCallback the callback that will be triggered when audio output is
+ * no longer available.
+ */
+ public void setMediaStreamingStatusCallback(MediaStreamingStatus.Callback mediaStreamingStatusCallback) {
+ this.mediaStreamingStatusCallback = mediaStreamingStatusCallback;
+ }
+
protected SdlProtocolBase getSdlProtocolImplementation() {
if (transportConfig instanceof MultiplexTransportConfig) {
return new SdlProtocol(this, (MultiplexTransportConfig) transportConfig);
@@ -94,8 +106,12 @@ public class SdlSession extends BaseSdlSession {
mediaStreamingStatus = new MediaStreamingStatus(contextWeakReference.get(), new MediaStreamingStatus.Callback() {
@Override
public void onAudioNoLongerAvailable() {
- close();
- shutdown("Audio output no longer available");
+ if (mediaStreamingStatusCallback != null) {
+ mediaStreamingStatusCallback.onAudioNoLongerAvailable();
+ } else {
+ close();
+ shutdown("Audio output no longer available");
+ }
}
});
}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java
index 7bed1b482..936989f5c 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java
@@ -34,6 +34,8 @@ package com.smartdevicelink.transport;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
+import android.app.ForegroundServiceStartNotAllowedException;
+import android.app.ServiceStartNotAllowedException;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
@@ -282,8 +284,8 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver {
restart.putExtra(LOCAL_ROUTER_SERVICE_DID_START_OWN, true);
context.sendBroadcast(restart);
- } catch (SecurityException e) {
- DebugTool.logError(TAG, "Security exception, process is bad");
+ } catch (SecurityException | IllegalStateException e) {
+ handleStartServiceException(e);
}
}
@@ -478,9 +480,8 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver {
} else {
context.startService(intent);
}
- } catch (SecurityException e) {
- DebugTool.logError(TAG, "Security exception, process is bad");
- // This service could not be started
+ } catch (SecurityException | IllegalStateException e) {
+ handleStartServiceException(e);
}
}
@@ -599,6 +600,27 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver {
return false;
}
+ /**
+ * Convenience method to log details on the specific exception that occurred while attempting to
+ * start a foreground service.
+ * @param e the exception that occurred
+ */
+ protected static void handleStartServiceException(Exception e) {
+ if (e instanceof SecurityException) {
+ DebugTool.logError(TAG, "Security exception, process is bad");
+ return;
+ } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
+ if (e instanceof ForegroundServiceStartNotAllowedException) {
+ DebugTool.logError(TAG, "Not allowed to start service in foreground");
+ return;
+ } else if (e instanceof ServiceStartNotAllowedException) {
+ DebugTool.logError(TAG, "Not allowed to start service in current state");
+ return;
+ }
+ }
+ DebugTool.logError(TAG, "Unable to start service for unknown reason");
+ }
+
private static SdlDeviceListener getSdlDeviceListener(Context context, BluetoothDevice bluetoothDevice) {
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterStatusProvider.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterStatusProvider.java
index b90a55684..078fe104c 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterStatusProvider.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterStatusProvider.java
@@ -136,11 +136,21 @@ public class SdlRouterStatusProvider {
} else {
bindingIntent.putExtra(FOREGROUND_EXTRA, true);
SdlBroadcastReceiver.setForegroundExceptionHandler(); //Prevent ANR in case the OS takes too long to start the service
- context.startForegroundService(bindingIntent);
+ try {
+ context.startForegroundService(bindingIntent);
+ } catch (SecurityException | IllegalStateException e) {
+ SdlBroadcastReceiver.handleStartServiceException(e);
+ }
}
bindingIntent.setAction(TransportConstants.BIND_REQUEST_TYPE_STATUS);
- return context.bindService(bindingIntent, routerConnection, Context.BIND_AUTO_CREATE);
+ boolean didBind = false;
+ try {
+ didBind = context.bindService(bindingIntent, routerConnection, Context.BIND_AUTO_CREATE);
+ } catch (SecurityException | IllegalStateException e) {
+ SdlBroadcastReceiver.handleStartServiceException(e);
+ }
+ return didBind;
}
private void unBindFromService() {
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/USBAccessoryAttachmentActivity.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/USBAccessoryAttachmentActivity.java
index 22ee5d1db..7bb2f753b 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/USBAccessoryAttachmentActivity.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/USBAccessoryAttachmentActivity.java
@@ -202,8 +202,8 @@ public class USBAccessoryAttachmentActivity extends Activity {
}
- } catch (SecurityException e) {
- DebugTool.logError(TAG, "Security exception, process is bad");
+ } catch (SecurityException | IllegalStateException e) {
+ SdlBroadcastReceiver.handleStartServiceException(e);
}
} else {
if (usbAccessory != null) {