diff options
author | Joey Grover <joeygrover@gmail.com> | 2022-04-13 11:03:25 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-13 11:03:25 -0400 |
commit | 16b2ab698a70294c0e060dd88883936a6288b6b8 (patch) | |
tree | b3b1fed7243708b656290d8496453cb01bb1b911 | |
parent | db5c9438505c4a3a64d2e6a16b36b3a61358a3e9 (diff) | |
parent | a174b5d63917985c428af4524ee167fcda6d4207 (diff) | |
download | sdl_android-16b2ab698a70294c0e060dd88883936a6288b6b8.tar.gz |
Merge pull request #1795 from smartdevicelink/5.4.0_RC5.4.0
Release 5.4.0
92 files changed, 1464 insertions, 588 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index c5a689a7b..f7f1182d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,57 @@ -# 5.3.1 Release Notes +# 5.4.0 Release Notes ## Summary: ||Version| |--|--| | **Protocol** | 5.4.1 | **RPC** | 8.0.0 -| **Tested Targeting** | Android 30 +| **Tested Targeting** | Android 31 ## Bug Fixes / Enhancements: -- [userDidSubmitInput getting called multiple times after a search request is submitted](https://github.com/smartdevicelink/sdl_java_suite/issues/1768) +- [Video auto restarts streaming regardless if HMI level is streamable ](https://github.com/smartdevicelink/sdl_java_suite/issues/1807) + +- [Video Streaming Fails on Android 12 at Low Resolutions](https://github.com/smartdevicelink/sdl_java_suite/issues/1803) + +- [sdl_java_se uses deprecated testCompile in build.gradle](https://github.com/smartdevicelink/sdl_java_suite/issues/1805) + +- [Incorrect behavior and potential crashes in PresentAlertOperation.supportsSoftButtonImages()](https://github.com/smartdevicelink/sdl_java_suite/issues/1800) + +- [Bugfix/rs notification issue usb](https://github.com/smartdevicelink/sdl_java_suite/pull/1797) + +- [Attempting to Send Voice Command with No Strings Fails to Log Error](https://github.com/smartdevicelink/sdl_java_suite/issues/1798) + +- [Fix LCM to close down properly](https://github.com/smartdevicelink/sdl_java_suite/pull/1796) + +- [Cleanup/proposal sdl 0286 spacing](https://github.com/smartdevicelink/sdl_java_suite/pull/1782) + +- [API Reference Docs Typos](https://github.com/smartdevicelink/sdl_java_suite/issues/1792) + +- [Foreground Service Notifications can be delayed by 10 seconds](https://github.com/smartdevicelink/sdl_java_suite/issues/1733) + +- [Bluetooth Permission Requirements will change in Android 12](https://github.com/smartdevicelink/sdl_java_suite/issues/1732) + +- [[SDL 0345] Android 12 Issues](https://github.com/smartdevicelink/sdl_java_suite/issues/1794) + +- [SdlRouterService crashes if SDL app sets targetSdkVersion to 31, and running on Android 12](https://github.com/smartdevicelink/sdl_java_suite/issues/1751) + +- [Error Code is cut off from bulk data when reading a SEND_INTERNAL_ERROR query](https://github.com/smartdevicelink/sdl_java_suite/issues/1790) + +- [NPE crash in BaseLifecycleManager on checkLifecycleConfiguration](https://github.com/smartdevicelink/sdl_java_suite/issues/1783) + +- [NPE crash in SdlDeviceListener](https://github.com/smartdevicelink/sdl_java_suite/issues/1780) + +- [NPE crash in BaseLifecycleManager](https://github.com/smartdevicelink/sdl_java_suite/issues/1781) + +- [SoftButtonObject doesn't handle error states correctly](https://github.com/smartdevicelink/sdl_java_suite/issues/1774) + +- [ScrollableMessage.timeout units should be specified in documentation](https://github.com/smartdevicelink/sdl_java_suite/issues/1775) + +- [Incorrect SPP error notification presented to user](https://github.com/smartdevicelink/sdl_java_suite/issues/1661) + +- [SecurityQuery error notification payload not set](https://github.com/smartdevicelink/sdl_java_suite/issues/1753) + +- [Menu Manager won't send submenu cell images on RPC versions >= 5.0 && < 7.0 #2047](https://github.com/smartdevicelink/sdl_java_suite/issues/1756) + +- [GenerateSources gradle task in javaSE incorrectly reads the version from gradle.properties instead of VERSION file](https://github.com/smartdevicelink/sdl_java_suite/issues/1763) -- [NPE in SoftButtonReplaceOperation](https://github.com/smartdevicelink/sdl_java_suite/issues/1767) @@ -1 +1 @@ -5.3.1 +5.4.0 diff --git a/android/hello_sdl_android/build.gradle b/android/hello_sdl_android/build.gradle index 7640e89c5..5feeba93f 100755 --- a/android/hello_sdl_android/build.gradle +++ b/android/hello_sdl_android/build.gradle @@ -1,11 +1,11 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 30 + compileSdkVersion 31 defaultConfig { applicationId "com.sdl.hellosdlandroid" minSdkVersion 16 - targetSdkVersion 30 + targetSdkVersion 31 versionCode 1 versionName "1.0" testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' diff --git a/android/hello_sdl_android/src/main/AndroidManifest.xml b/android/hello_sdl_android/src/main/AndroidManifest.xml index 1183e7b2c..415aa66c2 100755 --- a/android/hello_sdl_android/src/main/AndroidManifest.xml +++ b/android/hello_sdl_android/src/main/AndroidManifest.xml @@ -4,6 +4,8 @@ package="com.sdl.hellosdlandroid"> <uses-permission android:name="android.permission.BLUETOOTH" /> + <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" + tools:targetApi="31"/> <uses-permission android:name="android.permission.INTERNET" /> <!-- Required to check if WiFi is enabled --> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> @@ -30,6 +32,7 @@ tools:ignore="DeepLinks"> <activity android:name=".MainActivity" + android:exported="true" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> @@ -40,6 +43,7 @@ <activity android:name="com.smartdevicelink.transport.USBAccessoryAttachmentActivity" + android:exported="true" android:launchMode="singleTop"> <intent-filter> <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" /> @@ -52,6 +56,7 @@ <service android:name="com.sdl.hellosdlandroid.SdlService" + android:exported="true" android:foregroundServiceType="connectedDevice"> </service> <service 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 43ad79ac7..8497e3b73 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,27 +1,66 @@ package com.sdl.hellosdlandroid; 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; public class MainActivity extends AppCompatActivity { + private static final int REQUEST_CODE = 200; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - //If we are connected to a module we want to start our SdlService + + if (BuildConfig.TRANSPORT.equals("MULTI") || BuildConfig.TRANSPORT.equals("MULTI_HB")) { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !checkPermission()) { + requestPermission(); + return; + } + //If we are connected to a module we want to start our SdlService SdlReceiver.queryForConnectedService(this); - } else if (BuildConfig.TRANSPORT.equals("TCP")) { + } else if (BuildConfig.TRANSPORT.equals("TCP")){ Intent proxyIntent = new Intent(this, SdlService.class); startService(proxyIntent); } } + private boolean checkPermission() { + return PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(getApplicationContext(), BLUETOOTH_CONNECT); + } + + private void requestPermission() { + ActivityCompat.requestPermissions(this, new String[]{BLUETOOTH_CONNECT}, REQUEST_CODE); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + switch (requestCode) { + case REQUEST_CODE: + if (grantResults.length > 0) { + + boolean btConnectGranted = grantResults[0] == PackageManager.PERMISSION_GRANTED; + + if (btConnectGranted) { + SdlReceiver.queryForConnectedService(this); + } + } + break; + } + } + @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. diff --git a/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlReceiver.java b/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlReceiver.java index 32c0ab1d5..e130d3d78 100755 --- a/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlReceiver.java +++ b/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlReceiver.java @@ -1,11 +1,13 @@ package com.sdl.hellosdlandroid; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Build; import com.smartdevicelink.transport.SdlBroadcastReceiver; import com.smartdevicelink.transport.SdlRouterService; +import com.smartdevicelink.transport.TransportConstants; import com.smartdevicelink.util.DebugTool; public class SdlReceiver extends SdlBroadcastReceiver { @@ -16,13 +18,27 @@ public class SdlReceiver extends SdlBroadcastReceiver { DebugTool.logInfo(TAG, "SDL Enabled"); intent.setClass(context, SdlService.class); - // SdlService needs to be foregrounded in Android O and above - // This will prevent apps in the background from crashing when they try to start SdlService - // Because Android O doesn't allow background apps to start background services - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - context.startForegroundService(intent); + // Starting with Android S SdlService needs to be started from a foreground context. + // We will check the intent for a pendingIntent parcelable extra + // This pendingIntent allows us to start the SdlService from the context of the active router service which is in the foreground + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent pendingIntent = (PendingIntent) intent.getParcelableExtra(TransportConstants.PENDING_INTENT_EXTRA); + if (pendingIntent != null) { + try { + pendingIntent.send(context, 0, intent); + } catch (PendingIntent.CanceledException e) { + e.printStackTrace(); + } + } } else { - context.startService(intent); + // SdlService needs to be foregrounded in Android O and above + // This will prevent apps in the background from crashing when they try to start SdlService + // Because Android O doesn't allow background apps to start background services + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(intent); + } else { + context.startService(intent); + } } } @@ -35,4 +51,9 @@ public class SdlReceiver extends SdlBroadcastReceiver { public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); // Required if overriding this method } + + @Override + public String getSdlServiceName() { + return SdlService.class.getSimpleName(); + } }
\ No newline at end of file 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 c6e9ce3ef..59daf2050 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 @@ -108,10 +108,13 @@ public class SdlService extends Service { NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (notificationManager != null) { notificationManager.createNotificationChannel(channel); - Notification serviceNotification = new Notification.Builder(this, channel.getId()) + Notification.Builder builder = new Notification.Builder(this, channel.getId()) .setContentTitle("Connected through SDL") - .setSmallIcon(R.drawable.ic_sdl) - .build(); + .setSmallIcon(R.drawable.ic_sdl); + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + builder.setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE); + } + Notification serviceNotification = builder.build(); startForeground(FOREGROUND_SERVICE_ID, serviceNotification); } } diff --git a/android/sdl_android/build.gradle b/android/sdl_android/build.gradle index 489da844e..4f2548737 100644 --- a/android/sdl_android/build.gradle +++ b/android/sdl_android/build.gradle @@ -1,11 +1,11 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 30 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 - targetSdkVersion 30 - versionCode 21 + targetSdkVersion 31 + versionCode 22 versionName new File(projectDir.path, ('/../../VERSION')).text.trim() buildConfigField "String", "VERSION_NAME", '\"' + versionName + '\"' resValue "string", "SDL_LIB_VERSION", '\"' + versionName + '\"' @@ -40,6 +40,8 @@ android { } } +ext { VERSION_NAME = "$project.android.defaultConfig.versionName" } + dependencies { api fileTree(dir: 'libs', include: ['*.jar']) //api 'com.livio.taskmaster:taskmaster:0.6.0' diff --git a/android/sdl_android/gradle.properties b/android/sdl_android/gradle.properties index 3137d38b9..d720ac795 100644 --- a/android/sdl_android/gradle.properties +++ b/android/sdl_android/gradle.properties @@ -1,6 +1,5 @@ GROUP=com.smartdevicelink POM_ARTIFACT_ID=sdl_android -VERSION_NAME=5.3.1 POM_NAME=sdl_android POM_PACKAGING=aar diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertManagerTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertManagerTest.java index 1655b3d08..1ebac1567 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertManagerTest.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertManagerTest.java @@ -68,9 +68,9 @@ public class AlertManagerTest { public Void answer(InvocationOnMock invocation) { Object[] args = invocation.getArguments(); OnPermissionChangeListener onPermissionChangeListener = (OnPermissionChangeListener) args[2]; - Map<FunctionID, PermissionStatus > allowedPermissions = new HashMap<>(); + Map<FunctionID, PermissionStatus> allowedPermissions = new HashMap<>(); int permissionGroupStatus = PermissionManager.PERMISSION_GROUP_STATUS_DISALLOWED; - onPermissionChangeListener.onPermissionsChange(allowedPermissions,permissionGroupStatus); + onPermissionChangeListener.onPermissionsChange(allowedPermissions, permissionGroupStatus); return null; } }; 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 1283992a2..5f69010f1 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 @@ -194,10 +194,10 @@ public class PresentAlertOperationTest { windowCapability = getWindowCapability(2); - presentAlertOperation = new PresentAlertOperation(internalInterface, alertView, windowCapability, speechCapabilities, fileManager, 1, alertCompletionListener, alertSoftButtonClearListener); + presentAlertOperation = new PresentAlertOperation(internalInterface, alertView, windowCapability, speechCapabilities, fileManager, 1, alertCompletionListener, alertSoftButtonClearListener); alert = presentAlertOperation.alertRpc(); assertEquals(alert.getAlertText1(), alertView.getText()); - assertEquals(alert.getAlertText2(),alertView.getSecondaryText() + " - " + alertView.getTertiaryText()); + assertEquals(alert.getAlertText2(), alertView.getSecondaryText() + " - " + alertView.getTertiaryText()); } @Test diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/ScreenManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/ScreenManagerTests.java index b5cecf26c..b5c0059c4 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/ScreenManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/ScreenManagerTests.java @@ -209,22 +209,19 @@ public class ScreenManagerTests { SoftButtonObject softButtonObject3 = new SoftButtonObject("object1", Arrays.asList(softButtonState1, softButtonState2), softButtonState1.getName(), null); SoftButtonObject softButtonObject4 = new SoftButtonObject("object2", Arrays.asList(softButtonState3, softButtonState4), softButtonState3.getName(), null); assertTrue(screenManager.checkAndAssignButtonIds(softButtonObjects, BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER)); - - - - - } + @Test public void testAssigningIdsToSoftButtonObjects() { + SoftButtonState defaultState = new SoftButtonState("default", "hi", null); SoftButtonObject sbo1, sbo2, sbo3, sbo4, sbo5; // Case 1 - don't set id for any button (Manager should set ids automatically starting from 1 and up) - sbo1 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null); - sbo2 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null); - sbo3 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null); - sbo4 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null); - sbo5 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null); + sbo1 = new SoftButtonObject(null, defaultState, null); + sbo2 = new SoftButtonObject(null, defaultState, null); + sbo3 = new SoftButtonObject(null, defaultState, null); + sbo4 = new SoftButtonObject(null, defaultState, null); + sbo5 = new SoftButtonObject(null, defaultState, null); screenManager.checkAndAssignButtonIds(Arrays.asList(sbo1, sbo2, sbo3, sbo4, sbo5), BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER); assertEquals("SoftButtonObject id doesn't match the expected value", 1, sbo1.getButtonId()); assertEquals("SoftButtonObject id doesn't match the expected value", 2, sbo2.getButtonId()); @@ -234,15 +231,15 @@ public class ScreenManagerTests { // Case 2 - Set ids for all buttons (Manager shouldn't alter the ids set by developer) - sbo1 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null); + sbo1 = new SoftButtonObject(null, defaultState, null); sbo1.setButtonId(100); - sbo2 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null); + sbo2 = new SoftButtonObject(null, defaultState, null); sbo2.setButtonId(200); - sbo3 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null); + sbo3 = new SoftButtonObject(null, defaultState, null); sbo3.setButtonId(300); - sbo4 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null); + sbo4 = new SoftButtonObject(null, defaultState, null); sbo4.setButtonId(400); - sbo5 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null); + sbo5 = new SoftButtonObject(null, defaultState, null); sbo5.setButtonId(500); screenManager.checkAndAssignButtonIds(Arrays.asList(sbo1, sbo2, sbo3, sbo4, sbo5), BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER); assertEquals("SoftButtonObject id doesn't match the expected value", 100, sbo1.getButtonId()); @@ -253,13 +250,13 @@ public class ScreenManagerTests { // Case 3 - Set ids for some buttons (Manager shouldn't alter the ids set by developer. And it should assign ids for the ones that don't have id) - sbo1 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null); + sbo1 = new SoftButtonObject(null, defaultState, null); sbo1.setButtonId(50); - sbo2 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null); - sbo3 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null); - sbo4 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null); + sbo2 = new SoftButtonObject(null, defaultState, null); + sbo3 = new SoftButtonObject(null, defaultState, null); + sbo4 = new SoftButtonObject(null, defaultState, null); sbo4.setButtonId(100); - sbo5 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null); + sbo5 = new SoftButtonObject(null, defaultState, null); screenManager.checkAndAssignButtonIds(Arrays.asList(sbo1, sbo2, sbo3, sbo4, sbo5), BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER); assertEquals("SoftButtonObject id doesn't match the expected value", 50, sbo1.getButtonId()); assertEquals("SoftButtonObject id doesn't match the expected value", 101, sbo2.getButtonId()); diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SoftButtonManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SoftButtonManagerTests.java index becc01a74..67e7384e9 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SoftButtonManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SoftButtonManagerTests.java @@ -44,6 +44,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; + import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertNull; @@ -350,8 +351,8 @@ public class SoftButtonManagerTests { softButtonStateList.add(softButtonState1); softButtonStateList2.add(softButtonState1); softButtonStateList2.add(softButtonState2); - softButtonObject1 = new SoftButtonObject("hi", softButtonStateList, "Hi", null); - softButtonObject2 = new SoftButtonObject("hi", softButtonStateList2, "Hi", null); + softButtonObject1 = new SoftButtonObject("hi", softButtonStateList, softButtonStateList.get(0).getName(), null); + softButtonObject2 = new SoftButtonObject("hi", softButtonStateList2, softButtonStateList2.get(0).getName(), null); assertNotEquals(softButtonObject1, softButtonObject2); // Case 5: SoftButtonStates are not the same, assertFalse @@ -365,8 +366,8 @@ public class SoftButtonManagerTests { assertNotEquals(softButtonObject1, softButtonObject2); // Case 7: SoftButtonObject currentStateName not same, assertFalse - softButtonObject1 = new SoftButtonObject("hi", softButtonStateList, "Hi", null); - softButtonObject2 = new SoftButtonObject("hi", softButtonStateList, "Hi2", null); + softButtonObject1 = new SoftButtonObject("hi", softButtonStateList2, softButtonStateList2.get(0).getName(), null); + softButtonObject2 = new SoftButtonObject("hi", softButtonStateList2, softButtonStateList2.get(1).getName(), null); assertNotEquals(softButtonObject1, softButtonObject2); } @@ -402,4 +403,117 @@ public class SoftButtonManagerTests { softButtonState2 = new SoftButtonState("object1-state1", "o1s1", artwork1); assertEquals(softButtonState1, softButtonState2); } + + /** + * Test constructing SoftButtonObject with an empty state list + */ + @Test + public void testConstructSoftButtonObjectWithEmptyStateList() { + List<SoftButtonState> stateList = new ArrayList<>(); + SoftButtonObject softButtonObject = new SoftButtonObject("hello_there", stateList, "general_kenobi", null); + assertNull(softButtonObject.getStates()); + } + + /** + * Test constructing SoftButtonObject with an nonempty state list + */ + @Test + public void testConstructSoftButtonObjectWithNonEmptyStateList() { + List<SoftButtonState> stateList = new ArrayList<>(); + SoftButtonState softButtonState = new SoftButtonState("general_kenobi", "General Kenobi", null); + stateList.add(softButtonState); + SoftButtonObject softButtonObject = new SoftButtonObject("hello_there", stateList, "general_kenobi", null); + assertEquals(stateList, softButtonObject.getStates()); + } + + /** + * Test constructing SoftButtonObject with an invalid initialStateName + */ + @Test + public void testConstructSoftButtonObjectWithInvalidInitialStateName() { + List<SoftButtonState> stateList = new ArrayList<>(); + SoftButtonState softButtonState = new SoftButtonState("general_kenobi", "General Kenobi", null); + stateList.add(softButtonState); + SoftButtonObject softButtonObject = new SoftButtonObject("hello_there", stateList, "hello_there", null); + assertNull(softButtonObject.getStates()); + } + + /** + * Test assigning an empty state list to existing SoftButtonObject + */ + @Test + public void testAssignEmptyStateListToSoftButtonObject() { + List<SoftButtonState> nonEmptyStateList = new ArrayList<>(); + List<SoftButtonState> emptyStateList = new ArrayList<>(); + SoftButtonState softButtonState = new SoftButtonState("general_kenobi", "General Kenobi", null); + nonEmptyStateList.add(softButtonState); + + SoftButtonObject softButtonObject = new SoftButtonObject("hello_there", nonEmptyStateList, "general_kenobi", null); + + softButtonObject.setStates(emptyStateList); + assertEquals(nonEmptyStateList, softButtonObject.getStates()); + } + + /** + * Test assigning a state list with the current state to existing SoftButtonObject + */ + @Test + public void testAssignStateListWithCurrentStateToSoftButtonObject() { + List<SoftButtonState> stateList1 = new ArrayList<>(); + SoftButtonState softButtonState1 = new SoftButtonState("hello_there", "Hello there", null); + stateList1.add(softButtonState1); + + List<SoftButtonState> stateList2 = new ArrayList<>(); + SoftButtonState softButtonState2 = new SoftButtonState("general_kenobi", "General Kenobi", null); + stateList2.add(softButtonState1); + stateList2.add(softButtonState2); + + SoftButtonObject softButtonObject = new SoftButtonObject("general_kenobi", stateList1, "hello_there", null); + + softButtonObject.setStates(stateList2); + + assertEquals(stateList2, softButtonObject.getStates()); + } + + /** + * Test assigning a state list without the current state to existing SoftButtonObject + */ + @Test + public void testAssignStateListWithoutCurrentStateToSoftButtonObject() { + List<SoftButtonState> stateList1 = new ArrayList<>(); + SoftButtonState softButtonState1 = new SoftButtonState("hello_there", "Hello there", null); + stateList1.add(softButtonState1); + + List<SoftButtonState> stateList2 = new ArrayList<>(); + SoftButtonState softButtonState2 = new SoftButtonState("general_kenobi", "General Kenobi", null); + stateList2.add(softButtonState2); + + SoftButtonObject softButtonObject = new SoftButtonObject("general_kenobi", stateList1, "hello_there", null); + + softButtonObject.setStates(stateList2); + + assertEquals(stateList2, softButtonObject.getStates()); + } + + /** + * Test assigning a state list with states that have the same name to existing SoftButtonObject + */ + @Test + public void testAssignSameNameStateListToSoftButtonObject() { + List<SoftButtonState> stateListUnique = new ArrayList<>(); + SoftButtonState softButtonState1 = new SoftButtonState("hello_there", "Hello there", null); + stateListUnique.add(softButtonState1); + + List<SoftButtonState> stateListDuplicateNames = new ArrayList<>(); + SoftButtonState softButtonState2 = new SoftButtonState("general_kenobi", "General Kenobi", null); + stateListDuplicateNames.add(softButtonState2); + SoftButtonState softButtonState3 = new SoftButtonState("general_kenobi", "General Kenobi Again", null); + stateListDuplicateNames.add(softButtonState3); + + SoftButtonObject softButtonObject = new SoftButtonObject("general_kenobi", stateListUnique, "hello_there", null); + + softButtonObject.setStates(stateListDuplicateNames); + + assertEquals(stateListUnique, softButtonObject.getStates()); + } } 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 f26a7a5c5..1315d2b66 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 @@ -279,7 +279,7 @@ public class ChoiceSetManagerTests { newCSM.setKeyboardConfiguration(newCSM.defaultKeyboardConfiguration()); Field field = BaseChoiceSetManager.class.getDeclaredField("keyboardConfiguration"); field.setAccessible(true); - KeyboardProperties properties = (KeyboardProperties)field.get(newCSM); + KeyboardProperties properties = (KeyboardProperties) field.get(newCSM); assertEquals(properties, csm.defaultKeyboardConfiguration()); // Test presentKeyboard @@ -287,7 +287,7 @@ public class ChoiceSetManagerTests { newCSM.presentKeyboard("qwerty", newCSM.defaultKeyboardConfiguration(), null); field = BaseChoiceSetManager.class.getDeclaredField("keyboardConfiguration"); field.setAccessible(true); - properties = (KeyboardProperties)field.get(newCSM); + properties = (KeyboardProperties) field.get(newCSM); assertEquals(properties, csm.defaultKeyboardConfiguration()); } @@ -314,7 +314,7 @@ public class ChoiceSetManagerTests { Field field = BaseChoiceSetManager.class.getDeclaredField("keyboardConfiguration"); field.setAccessible(true); - KeyboardProperties getProperties = (KeyboardProperties)field.get(newCSM); + KeyboardProperties getProperties = (KeyboardProperties) field.get(newCSM); assertEquals(getProperties.getCustomKeys().size(), 1); } @@ -342,7 +342,7 @@ public class ChoiceSetManagerTests { Field field = BaseChoiceSetManager.class.getDeclaredField("keyboardConfiguration"); field.setAccessible(true); - KeyboardProperties getProperties = (KeyboardProperties)field.get(newCSM); + KeyboardProperties getProperties = (KeyboardProperties) field.get(newCSM); assertNull(getProperties.getCustomKeys()); } @@ -373,7 +373,7 @@ public class ChoiceSetManagerTests { Field field = BaseChoiceSetManager.class.getDeclaredField("keyboardConfiguration"); field.setAccessible(true); - KeyboardProperties getProperties = (KeyboardProperties)field.get(newCSM); + KeyboardProperties getProperties = (KeyboardProperties) field.get(newCSM); assertNull(getProperties.getMaskInputCharacters()); } 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 index 0f3725f53..8a8b8c64e 100644 --- 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 @@ -344,7 +344,7 @@ public class PreloadPresentChoicesOperationTests { 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 = 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()); @@ -360,7 +360,7 @@ public class PreloadPresentChoicesOperationTests { 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 = 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()); diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperationTests.java index c52cf6186..c6281652f 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperationTests.java @@ -192,7 +192,7 @@ public class MenuConfigurationUpdateOperationTests { transactionQueue.add(operation, false); } - private Answer<Void> createSetGlobalPropertiesAnswer(final boolean success){ + private Answer<Void> createSetGlobalPropertiesAnswer(final boolean success) { return new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) { @@ -206,7 +206,7 @@ public class MenuConfigurationUpdateOperationTests { }; } - private WindowCapability createWindowCapability (boolean supportsList, boolean supportsTile) { + private WindowCapability createWindowCapability(boolean supportsList, boolean supportsTile) { WindowCapability windowCapability = new WindowCapability(); windowCapability.setMenuLayoutsAvailable(new ArrayList<MenuLayout>()); if (supportsList) { diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java index 6f8bff69d..863f0b4d9 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java @@ -145,7 +145,7 @@ public class MenuManagerTests { @Override public Void answer(InvocationOnMock invocation) { Object[] args = invocation.getArguments(); - List<RPCMessage> rpcs = (List<RPCMessage>) args[0]; + List<RPCMessage> rpcs = (List<RPCMessage>) args[0]; OnMultipleRequestListener listener = (OnMultipleRequestListener) args[1]; for (RPCMessage rpcMessage : rpcs) { @@ -261,12 +261,15 @@ public class MenuManagerTests { @Test public void testUpdatingOldWay() { + ISdl internalInterface = mock(ISdl.class); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(8, 0, 0))); + // Force Menu Manager to use the old way of deleting / sending all menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_OFF); assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_OFF); // when we only send one command to update, we should only be returned one add command List<MenuCell> newArray = Arrays.asList(mainCell1, mainCell4); - assertEquals(MenuReplaceUtilities.allCommandsForCells(newArray, menuManager.fileManager.get(), menuManager.windowCapability, MenuLayout.LIST).size(), 4); // 1 root cells, 1 sub menu root cell, 2 sub menu cells + assertEquals(MenuReplaceUtilities.allCommandsForCells(internalInterface, newArray, menuManager.fileManager.get(), menuManager.windowCapability, MenuLayout.LIST).size(), 4); // 1 root cells, 1 sub menu root cell, 2 sub menu cells menuManager.currentHMILevel = HMILevel.HMI_FULL; menuManager.setMenuCells(newArray); @@ -547,7 +550,7 @@ public class MenuManagerTests { // Sleep to give time to Taskmaster to run the operations sleep(); - assertEquals(0 , menuManager.currentMenuCells.size()); + assertEquals(0, menuManager.currentMenuCells.size()); } @Test diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java index 61587dbdc..0c7285531 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java @@ -34,13 +34,16 @@ package com.smartdevicelink.managers.screen.menu; 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.proxy.rpc.ImageField; +import com.smartdevicelink.proxy.rpc.SdlMsgVersion; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; import com.smartdevicelink.test.TestValues; +import com.smartdevicelink.util.Version; import org.junit.Before; import org.junit.Test; @@ -210,7 +213,88 @@ public class MenuReplaceUtilitiesTests { } @Test + public void testWindowCapabilitySupportsPrimaryImage() { + WindowCapability windowCapability; + ISdl internalInterface = mock(ISdl.class); + MenuCell menuCell = mock(MenuCell.class); + + // Test case 0 + windowCapability = createWindowCapability(false, true); + when(menuCell.isSubMenuCell()).thenReturn(true); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(4, 9, 0))); + assertTrue(MenuReplaceUtilities.windowCapabilitySupportsPrimaryImage(internalInterface, windowCapability, menuCell)); + + // Test case 1 + windowCapability = createWindowCapability(false, false); + when(menuCell.isSubMenuCell()).thenReturn(true); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(4, 9, 0))); + assertFalse(MenuReplaceUtilities.windowCapabilitySupportsPrimaryImage(internalInterface, windowCapability, menuCell)); + + // Test case 2 + windowCapability = createWindowCapability(false, false); + when(menuCell.isSubMenuCell()).thenReturn(true); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(5, 0, 0))); + assertFalse(MenuReplaceUtilities.windowCapabilitySupportsPrimaryImage(internalInterface, windowCapability, menuCell)); + + // Test case 3 + windowCapability = createWindowCapability(true, false); + when(menuCell.isSubMenuCell()).thenReturn(true); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(5, 0, 0))); + assertTrue(MenuReplaceUtilities.windowCapabilitySupportsPrimaryImage(internalInterface, windowCapability, menuCell)); + + // Test case 4 + windowCapability = createWindowCapability(false, false); + when(menuCell.isSubMenuCell()).thenReturn(true); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(6, 0, 0))); + assertFalse(MenuReplaceUtilities.windowCapabilitySupportsPrimaryImage(internalInterface, windowCapability, menuCell)); + + // Test case 5 + windowCapability = createWindowCapability(true, false); + when(menuCell.isSubMenuCell()).thenReturn(true); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(6, 0, 0))); + assertTrue(MenuReplaceUtilities.windowCapabilitySupportsPrimaryImage(internalInterface, windowCapability, menuCell)); + + // Test case 6 + windowCapability = createWindowCapability(false, false); + when(menuCell.isSubMenuCell()).thenReturn(true); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(7, 0, 0))); + assertFalse(MenuReplaceUtilities.windowCapabilitySupportsPrimaryImage(internalInterface, windowCapability, menuCell)); + + // Test case 7 + windowCapability = createWindowCapability(false, false); + when(menuCell.isSubMenuCell()).thenReturn(true); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(7, 1, 0))); + assertFalse(MenuReplaceUtilities.windowCapabilitySupportsPrimaryImage(internalInterface, windowCapability, menuCell)); + + // Test case 8 + windowCapability = createWindowCapability(false, false); + when(menuCell.isSubMenuCell()).thenReturn(true); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(8, 0, 0))); + assertFalse(MenuReplaceUtilities.windowCapabilitySupportsPrimaryImage(internalInterface, windowCapability, menuCell)); + + // Test case 9 + windowCapability = createWindowCapability(false, true); + when(menuCell.isSubMenuCell()).thenReturn(true); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(8, 0, 0))); + assertTrue(MenuReplaceUtilities.windowCapabilitySupportsPrimaryImage(internalInterface, windowCapability, menuCell)); + + // Test case 10 + windowCapability = createWindowCapability(false, false); + when(menuCell.isSubMenuCell()).thenReturn(false); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(8, 0, 0))); + assertFalse(MenuReplaceUtilities.windowCapabilitySupportsPrimaryImage(internalInterface, windowCapability, menuCell)); + + // Test case 11 + windowCapability = createWindowCapability(true, false); + when(menuCell.isSubMenuCell()).thenReturn(false); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(8, 0, 0))); + assertTrue(MenuReplaceUtilities.windowCapabilitySupportsPrimaryImage(internalInterface, windowCapability, menuCell)); + } + + @Test public void testShouldCellIncludeImage() { + ISdl internalInterface = mock(ISdl.class); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(8, 0, 0))); MenuCell menuCell; WindowCapability windowCapability; FileManager fileManager; @@ -220,34 +304,34 @@ public class MenuReplaceUtilitiesTests { menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, voiceCommands, null); windowCapability = createWindowCapability(true, true); fileManager = createMockFileManager(true); - assertTrue(MenuReplaceUtilities.shouldCellIncludePrimaryImageFromCell(menuCell, fileManager, windowCapability)); + assertTrue(MenuReplaceUtilities.shouldCellIncludePrimaryImageFromCell(internalInterface, menuCell, fileManager, windowCapability)); // Case 2 - Image are not supported menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, voiceCommands, null); windowCapability = createWindowCapability(false, false); fileManager = createMockFileManager(true); - assertFalse(MenuReplaceUtilities.shouldCellIncludePrimaryImageFromCell(menuCell, fileManager, windowCapability)); + assertFalse(MenuReplaceUtilities.shouldCellIncludePrimaryImageFromCell(internalInterface, menuCell, fileManager, windowCapability)); // Case 3 - Artwork is null menuCell = new MenuCell(TestValues.GENERAL_STRING, null, voiceCommands, null); windowCapability = createWindowCapability(true, true); fileManager = createMockFileManager(true); - assertFalse(MenuReplaceUtilities.shouldCellIncludePrimaryImageFromCell(menuCell, fileManager, windowCapability)); + assertFalse(MenuReplaceUtilities.shouldCellIncludePrimaryImageFromCell(internalInterface, menuCell, fileManager, windowCapability)); // Case 4 - Artwork has not been uploaded menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, voiceCommands, null); windowCapability = createWindowCapability(true, true); fileManager = createMockFileManager(false); - assertFalse(MenuReplaceUtilities.shouldCellIncludePrimaryImageFromCell(menuCell, fileManager, windowCapability)); + assertFalse(MenuReplaceUtilities.shouldCellIncludePrimaryImageFromCell(internalInterface, menuCell, fileManager, windowCapability)); // Case 5 - Artwork is static icon menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK_STATIC, voiceCommands, null); windowCapability = createWindowCapability(true, true); fileManager = createMockFileManager(false); - assertTrue(MenuReplaceUtilities.shouldCellIncludePrimaryImageFromCell(menuCell, fileManager, windowCapability)); + assertTrue(MenuReplaceUtilities.shouldCellIncludePrimaryImageFromCell(internalInterface, menuCell, fileManager, windowCapability)); } - private WindowCapability createWindowCapability (boolean supportsCmdIcon, boolean supportsSubMenuIcon) { + private WindowCapability createWindowCapability(boolean supportsCmdIcon, boolean supportsSubMenuIcon) { WindowCapability windowCapability = new WindowCapability(); windowCapability.setImageFields(new ArrayList<ImageField>()); if (supportsCmdIcon) { @@ -259,7 +343,7 @@ public class MenuReplaceUtilitiesTests { return windowCapability; } - private FileManager createMockFileManager (boolean hasUploadedFile) { + private FileManager createMockFileManager(boolean hasUploadedFile) { FileManager fileManager = mock(FileManager.class); when(fileManager.hasUploadedFile(any(SdlArtwork.class))).thenReturn(hasUploadedFile); return fileManager; @@ -308,7 +392,7 @@ public class MenuReplaceUtilitiesTests { List<MenuCell> menuCellList = new ArrayList<>(Arrays.asList(menuCell1, menuCell2, menuCell3, menuCell4)); addIdsToMenuCells(menuCellList, parentIdNotFound); - return menuCellList ; + return menuCellList; } private List<MenuCell> createNewMenuList() { @@ -338,6 +422,6 @@ public class MenuReplaceUtilitiesTests { List<MenuCell> newMenuList = new ArrayList<>(Arrays.asList(menuCell5)); addIdsToMenuCells(newMenuList, parentIdNotFound); - return newMenuList ; + return newMenuList; } } diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuShowOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuShowOperationTests.java index ee90cee49..29c9b7ce4 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuShowOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuShowOperationTests.java @@ -108,7 +108,7 @@ public class MenuShowOperationTests { verify(internalInterface, Mockito.times(1)).sendRPC(any(ShowAppMenu.class)); } - private Answer<Void> createShowAppMenuAnswer(final boolean success, final Integer menuIdToAssert){ + private Answer<Void> createShowAppMenuAnswer(final boolean success, final Integer menuIdToAssert) { return new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) { @@ -118,7 +118,7 @@ public class MenuShowOperationTests { @Override public void run() { ShowAppMenu showAppMenu = (ShowAppMenu) request; - assertEquals(showAppMenu.getMenuID(), menuIdToAssert); + assertEquals(showAppMenu.getMenuID(), menuIdToAssert); } }); RPCResponse response = new RPCResponse(request.getFunctionID().toString()); diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperationTests.java index 67885a902..475e29983 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperationTests.java @@ -117,13 +117,13 @@ public class VoiceCommandUpdateOperationTests { AddCommand addCommand = null; try { - deleteCommand = (DeleteCommand) ((List<Object>)invocation.getArguments()[0]).get(0); + deleteCommand = (DeleteCommand) ((List<Object>) invocation.getArguments()[0]).get(0); } catch (Exception e) { DebugTool.logInfo(TAG, "not DeleteCommands: " + e); } try { - addCommand = (AddCommand) ((List<Object>)invocation.getArguments()[0]).get(0); + addCommand = (AddCommand) ((List<Object>) invocation.getArguments()[0]).get(0); } catch (Exception e) { DebugTool.logInfo(TAG, "not AddCommands: " + e); } @@ -131,24 +131,24 @@ public class VoiceCommandUpdateOperationTests { if (deleteCommand != null) { DeleteCommandResponse badResponse = new DeleteCommandResponse(); badResponse.setSuccess(false); - List<DeleteCommand> deleteCommands = ((List<DeleteCommand>)invocation.getArguments()[0]); + List<DeleteCommand> deleteCommands = ((List<DeleteCommand>) invocation.getArguments()[0]); for (DeleteCommand command : deleteCommands) { badResponse.setCorrelationID(command.getCorrelationID()); - ((OnMultipleRequestListener)invocation.getArguments()[1]).onResponse(command.getCorrelationID(), badResponse); + ((OnMultipleRequestListener) invocation.getArguments()[1]).onResponse(command.getCorrelationID(), badResponse); } } else if (addCommand != null) { AddCommandResponse badResponse = new AddCommandResponse(); badResponse.setSuccess(false); - List<AddCommand> addCommands = ((List<AddCommand>)invocation.getArguments()[0]); + List<AddCommand> addCommands = ((List<AddCommand>) invocation.getArguments()[0]); for (AddCommand command : addCommands) { badResponse.setCorrelationID(command.getCorrelationID()); - ((OnMultipleRequestListener)invocation.getArguments()[1]).onResponse(command.getCorrelationID(), badResponse); + ((OnMultipleRequestListener) invocation.getArguments()[1]).onResponse(command.getCorrelationID(), badResponse); } } else { DebugTool.logInfo(TAG, "CallBacks failed"); return null; } - ((OnMultipleRequestListener)invocation.getArguments()[1]).onFinished(); + ((OnMultipleRequestListener) invocation.getArguments()[1]).onFinished(); return null; } }).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class)); @@ -181,13 +181,13 @@ public class VoiceCommandUpdateOperationTests { AddCommand addCommand = null; try { - deleteCommand = (DeleteCommand) ((List<Object>)invocation.getArguments()[0]).get(0); + deleteCommand = (DeleteCommand) ((List<Object>) invocation.getArguments()[0]).get(0); } catch (Exception e) { DebugTool.logInfo(TAG, "not DeleteCommands: " + e); } try { - addCommand = (AddCommand) ((List<Object>)invocation.getArguments()[0]).get(0); + addCommand = (AddCommand) ((List<Object>) invocation.getArguments()[0]).get(0); } catch (Exception e) { DebugTool.logInfo(TAG, "not AddCommands: " + e); } @@ -195,24 +195,24 @@ public class VoiceCommandUpdateOperationTests { if (deleteCommand != null) { DeleteCommandResponse successResponse = new DeleteCommandResponse(); successResponse.setSuccess(true); - List<DeleteCommand> deleteCommands = ((List<DeleteCommand>)invocation.getArguments()[0]); + List<DeleteCommand> deleteCommands = ((List<DeleteCommand>) invocation.getArguments()[0]); for (DeleteCommand command : deleteCommands) { successResponse.setCorrelationID(command.getCorrelationID()); - ((OnMultipleRequestListener)invocation.getArguments()[1]).onResponse(command.getCorrelationID(), successResponse); + ((OnMultipleRequestListener) invocation.getArguments()[1]).onResponse(command.getCorrelationID(), successResponse); } } else if (addCommand != null) { AddCommandResponse successResponse = new AddCommandResponse(); successResponse.setSuccess(true); - List<AddCommand> addCommands = ((List<AddCommand>)invocation.getArguments()[0]); + List<AddCommand> addCommands = ((List<AddCommand>) invocation.getArguments()[0]); for (AddCommand command : addCommands) { successResponse.setCorrelationID(command.getCorrelationID()); - ((OnMultipleRequestListener)invocation.getArguments()[1]).onResponse(command.getCorrelationID(), successResponse); + ((OnMultipleRequestListener) invocation.getArguments()[1]).onResponse(command.getCorrelationID(), successResponse); } } else { DebugTool.logInfo(TAG, "CallBacks failed"); return null; } - ((OnMultipleRequestListener)invocation.getArguments()[1]).onFinished(); + ((OnMultipleRequestListener) invocation.getArguments()[1]).onFinished(); return null; } }).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class)); @@ -245,13 +245,13 @@ public class VoiceCommandUpdateOperationTests { AddCommand addCommand = null; try { - deleteCommand = (DeleteCommand) ((List<Object>)invocation.getArguments()[0]).get(0); + deleteCommand = (DeleteCommand) ((List<Object>) invocation.getArguments()[0]).get(0); } catch (Exception e) { DebugTool.logInfo(TAG, "not DeleteCommands: " + e); } try { - addCommand = (AddCommand) ((List<Object>)invocation.getArguments()[0]).get(0); + addCommand = (AddCommand) ((List<Object>) invocation.getArguments()[0]).get(0); } catch (Exception e) { DebugTool.logInfo(TAG, "not AddCommands: " + e); } @@ -259,24 +259,24 @@ public class VoiceCommandUpdateOperationTests { if (deleteCommand != null) { DeleteCommandResponse successResponse = new DeleteCommandResponse(); successResponse.setSuccess(true); - List<DeleteCommand> deleteCommands = ((List<DeleteCommand>)invocation.getArguments()[0]); + List<DeleteCommand> deleteCommands = ((List<DeleteCommand>) invocation.getArguments()[0]); for (DeleteCommand command : deleteCommands) { successResponse.setCorrelationID(command.getCorrelationID()); - ((OnMultipleRequestListener)invocation.getArguments()[1]).onResponse(command.getCorrelationID(), successResponse); + ((OnMultipleRequestListener) invocation.getArguments()[1]).onResponse(command.getCorrelationID(), successResponse); } } else if (addCommand != null) { AddCommandResponse successResponse = new AddCommandResponse(); successResponse.setSuccess(true); - List<AddCommand> addCommands = ((List<AddCommand>)invocation.getArguments()[0]); + List<AddCommand> addCommands = ((List<AddCommand>) invocation.getArguments()[0]); for (AddCommand command : addCommands) { successResponse.setCorrelationID(command.getCorrelationID()); - ((OnMultipleRequestListener)invocation.getArguments()[1]).onResponse(command.getCorrelationID(), successResponse); + ((OnMultipleRequestListener) invocation.getArguments()[1]).onResponse(command.getCorrelationID(), successResponse); } } else { DebugTool.logInfo(TAG, "CallBacks failed"); return null; } - ((OnMultipleRequestListener)invocation.getArguments()[1]).onFinished(); + ((OnMultipleRequestListener) invocation.getArguments()[1]).onFinished(); return null; } }).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class)); @@ -298,7 +298,6 @@ public class VoiceCommandUpdateOperationTests { } - @Test public void testVoiceCommandsInListNotInSecondList() { VoiceCommand command1 = new VoiceCommand(Collections.singletonList("Command 1"), null); @@ -314,7 +313,7 @@ public class VoiceCommandUpdateOperationTests { List<VoiceCommand> voiceCommandList2 = new ArrayList<>(); voiceCommandList2.add(command1Clone); voiceCommandList2.add(command3); - VoiceCommandUpdateOperation voiceCommandUpdateOperation = new VoiceCommandUpdateOperation(internalInterface,null,null,null); + VoiceCommandUpdateOperation voiceCommandUpdateOperation = new VoiceCommandUpdateOperation(internalInterface, null, null, null); List<VoiceCommand> differencesList = voiceCommandUpdateOperation.voiceCommandsInListNotInSecondList(voiceCommandList, voiceCommandList2); assertEquals(differencesList.size(), 1); diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/TestValues.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/TestValues.java index da3bb5846..261d121a4 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/TestValues.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/TestValues.java @@ -1064,7 +1064,7 @@ public class TestValues { JSON_GENERAL_SEATS_OCCUPIED.put(JSON_OBJECT_GENERAL_SEATS_OCCUPIED); JSON_OBJECT_GENERAL_SEATS_BELTED.put(SeatStatus.KEY_SEAT_LOCATION, GENERAL_SEATLOCATION.serializeJSON()); - JSON_OBJECT_GENERAL_SEATS_BELTED.put(SeatStatus.KEY_CONDITION_ACTIVE,GENERAL_BOOLEAN); + JSON_OBJECT_GENERAL_SEATS_BELTED.put(SeatStatus.KEY_CONDITION_ACTIVE, GENERAL_BOOLEAN); JSON_GENERAL_SEATS_BELTED.put(JSON_OBJECT_GENERAL_SEATS_BELTED); JSON_HMIPERMISSIONS.put(HMIPermissions.KEY_ALLOWED, GENERAL_HMILEVEL_LIST); 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 094b29794..235a9dfff 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 @@ -2431,7 +2431,7 @@ public class Validator { log("validatePermissionItem", "Items are the same object. No defensive copy took place."); return false; } - */ + */ if (!item1.getRpcName().equals(item2.getRpcName())) { return false; } @@ -2509,7 +2509,7 @@ public class Validator { log("validateParameterPermissions", "Items are the same object. No defensive copy took place."); return false; } - */ + */ if (!validateStringList(item1.getAllowed(), item2.getAllowed())) { return false; } 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 index 8c0c36041..c39e9bb42 100644 --- 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 @@ -25,7 +25,7 @@ public class SecurityQueryPayloadTests { bqh.setQueryID(SecurityQueryID.SEND_HANDSHAKE_DATA); bqh.setQueryType(SecurityQueryType.REQUEST); bqh.setBulkData(null); - bqh.setJsonSize(0); + bqh.setJsonData(null); return bqh; } @@ -66,9 +66,9 @@ public class SecurityQueryPayloadTests { dummyBqh.setQueryType(SecurityQueryType.REQUEST); dummyBqh.setQueryID(SecurityQueryID.SEND_HANDSHAKE_DATA); dummyBqh.setCorrelationID(3); - dummyBqh.setJsonSize(0); + dummyBqh.setJsonData(new byte[0]); - byte[] assembledHeader = dummyBqh.assembleHeaderBytes(); + byte[] assembledHeader = dummyBqh.assembleBinaryData(); assertEquals(dummyBqh.getQueryType(), SecurityQueryType.valueOf(assembledHeader[0])); byte[] queryIDFromHeader = new byte[3]; System.arraycopy(assembledHeader, 1, queryIDFromHeader, 0, 3); @@ -81,7 +81,7 @@ public class SecurityQueryPayloadTests { public void testAssemblyAndParse() { SecurityQueryPayload bqh = createDummyBqh(); - byte[] bqhBytes = bqh.assembleHeaderBytes(); + byte[] bqhBytes = bqh.assembleBinaryData(); assertNotNull(bqhBytes); SecurityQueryPayload parsedBqh = SecurityQueryPayload.parseBinaryQueryHeader(bqhBytes); @@ -99,7 +99,7 @@ public class SecurityQueryPayloadTests { public void testCorruptHeader() { SecurityQueryPayload bqh = createDummyBqh(); - byte[] bqhBytes = bqh.assembleHeaderBytes(); + byte[] bqhBytes = bqh.assembleBinaryData(); assertNotNull(safeParse(bqhBytes)); @@ -114,13 +114,18 @@ public class SecurityQueryPayloadTests { } @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 - } + public void testNullJsonData() { + SecurityQueryPayload bqh = createDummyBqh(); + bqh.setJsonData(null); + assertEquals(0, bqh.getJsonSize()); + assertEquals(null, bqh.getJsonData()); + } + + @Test + public void testNullBulkData() { + SecurityQueryPayload bqh = createDummyBqh(); + bqh.setBulkData(null); + assertEquals(0, bqh.getBulkDataSize()); + assertEquals(null, bqh.getBulkData()); } } 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 index 4f73f8139..6e1ecf762 100644 --- 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 @@ -39,7 +39,7 @@ public class SecurityQueryIDTests extends TestCase { 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) { + } catch (NullPointerException exception) { fail("Null enum list throws NullPointerException."); } } @@ -67,7 +67,7 @@ public class SecurityQueryIDTests extends TestCase { enumNull = (SecurityQueryID) SecurityQueryID.get(list, (byte[]) null); assertNull("Null lookup returns a null byte[] value", enumNull); - }catch (NullPointerException exception) { + } catch (NullPointerException exception) { fail("Null string throws NullPointerException."); } } 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 index 6835da946..2567bbb18 100644 --- 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 @@ -48,7 +48,7 @@ public class SecurityQueryTypeTests extends TestCase { assertNotNull("Invalid Query string byte match returned null", enumInvalidSecurityQueryType); - }catch (NullPointerException exception) { + } catch (NullPointerException exception) { fail("Null enum list throws NullPointerException."); } } diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/RPCGenericTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/RPCGenericTests.java index e4a61b9ed..2fa325419 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/RPCGenericTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/RPCGenericTests.java @@ -523,7 +523,7 @@ public class RPCGenericTests { assertTrue("The following RPCs don't have a constructor that has all the mandatory params: " + rpcsWithInvalidConstructor, rpcsWithInvalidConstructor.isEmpty()); } - private boolean annotationExists (AnnotatedElement element, String annotationName) { + private boolean annotationExists(AnnotatedElement element, String annotationName) { for (Annotation annotation : element.getDeclaredAnnotations()) { if (annotation.annotationType().getSimpleName().equalsIgnoreCase(annotationName)) { return true; @@ -532,7 +532,7 @@ public class RPCGenericTests { return false; } - private boolean isDeprecated (AnnotatedElement element) { + private boolean isDeprecated(AnnotatedElement element) { return annotationExists(element, "Deprecated"); } @@ -541,7 +541,7 @@ public class RPCGenericTests { for (Object anEnum : EnumSet.allOf(aClass)) { if (anEnum.toString().equals(elementName)) { try { - String value = ((Enum)anEnum).name(); + String value = ((Enum) anEnum).name(); field = aClass.getField(value); break; } catch (NoSuchFieldException e) { diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/AppCapabilityTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/AppCapabilityTests.java index 225dc393c..66099312e 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/AppCapabilityTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/AppCapabilityTests.java @@ -33,7 +33,7 @@ public class AppCapabilityTests extends TestCase { /** * Tests the expected values of the RPC message. */ - public void testRpcValues () { + public void testRpcValues() { // Test Values VideoStreamingCapability videoStreamingCapability = msg.getVideoStreamingCapability(); AppCapabilityType videoStreamingCapabilityType = msg.getAppCapabilityType(); @@ -50,10 +50,10 @@ public class AppCapabilityTests extends TestCase { assertNull(TestValues.NULL, msg.getVideoStreamingCapability()); } - public void testJson(){ + public void testJson() { JSONObject reference = new JSONObject(); - try{ + try { reference.put(KEY_APP_CAPABILITY_TYPE, TestValues.GENERAL_APPCAPABILITYTYPE); reference.put(KEY_VIDEO_STREAMING_CAPABILITY, TestValues.GENERAL_VIDEOSTREAMINGCAPABILITY); @@ -61,7 +61,7 @@ public class AppCapabilityTests extends TestCase { assertEquals(TestValues.MATCH, reference.length(), underTest.length()); Iterator<?> iterator = reference.keys(); - while(iterator.hasNext()){ + while (iterator.hasNext()) { String key = (String) iterator.next(); @@ -83,7 +83,7 @@ public class AppCapabilityTests extends TestCase { ); } } - } catch(JSONException e){ + } catch (JSONException e) { fail(TestValues.JSON_FAIL); } } diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/RoofStatusTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/RoofStatusTests.java index 4afc9d778..4c52ab7df 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/RoofStatusTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/RoofStatusTests.java @@ -54,13 +54,13 @@ public class RoofStatusTests extends TestCase { while (iterator.hasNext()) { String key = (String) iterator.next(); if (key.equals(RoofStatus.KEY_STATE)) { - Hashtable<String, Object> hs1 = JsonRPCMarshaller.deserializeJSONObject((JSONObject) JsonUtils.readObjectFromJsonObject(reference, key)); - Hashtable<String, Object> hs2 = JsonRPCMarshaller.deserializeJSONObject((JSONObject) JsonUtils.readObjectFromJsonObject(underTest, key)); + Hashtable<String, Object> hs1 = JsonRPCMarshaller.deserializeJSONObject((JSONObject) JsonUtils.readObjectFromJsonObject(reference, key)); + Hashtable<String, Object> hs2 = JsonRPCMarshaller.deserializeJSONObject((JSONObject) JsonUtils.readObjectFromJsonObject(underTest, key)); assertTrue(Validator.validateWindowStates(new WindowState(hs1), new WindowState(hs2))); } else if (key.equals(RoofStatus.KEY_LOCATION)) { - Hashtable<String, Object> hs1 = JsonRPCMarshaller.deserializeJSONObject((JSONObject) JsonUtils.readObjectFromJsonObject(reference, key)); - Hashtable<String, Object> hs2 = JsonRPCMarshaller.deserializeJSONObject((JSONObject) JsonUtils.readObjectFromJsonObject(underTest, key)); + Hashtable<String, Object> hs1 = JsonRPCMarshaller.deserializeJSONObject((JSONObject) JsonUtils.readObjectFromJsonObject(reference, key)); + Hashtable<String, Object> hs2 = JsonRPCMarshaller.deserializeJSONObject((JSONObject) JsonUtils.readObjectFromJsonObject(underTest, key)); assertTrue(Validator.validateGrid(new Grid(hs1), new Grid(hs2))); diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/VideoStreamingCapabilityTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/VideoStreamingCapabilityTests.java index c13199223..1bcb659a4 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/VideoStreamingCapabilityTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/VideoStreamingCapabilityTests.java @@ -35,7 +35,7 @@ public class VideoStreamingCapabilityTests extends TestCase { msg.setPixelPerInch(TestValues.GENERAL_DOUBLE); msg.setScale(TestValues.GENERAL_DOUBLE); msg.setAdditionalVideoStreamingCapabilities(TestValues.GENERAL_ADDITIONAL_CAPABILITY_LIST); - msg.setPreferredFPS(TestValues.GENERAL_INTEGER); + msg.setPreferredFPS(TestValues.GENERAL_INTEGER); } /** @@ -51,7 +51,7 @@ public class VideoStreamingCapabilityTests extends TestCase { Double pixelPerInch = msg.getPixelPerInch(); Double scale = msg.getScale(); List<VideoStreamingCapability> additionalVideoStreamingCapabilities = msg.getAdditionalVideoStreamingCapabilities(); - Integer preferredFPS = msg.getPreferredFPS(); + Integer preferredFPS = msg.getPreferredFPS(); // Valid Tests assertEquals(TestValues.MATCH, (List<VideoStreamingFormat>) TestValues.GENERAL_VIDEOSTREAMINGFORMAT_LIST, format); @@ -75,7 +75,7 @@ public class VideoStreamingCapabilityTests extends TestCase { assertNull(TestValues.NULL, msg.getPixelPerInch()); assertNull(TestValues.NULL, msg.getScale()); assertNull(TestValues.NULL, msg.getAdditionalVideoStreamingCapabilities()); - assertNull(TestValues.NULL, msg.getPreferredFPS()); + assertNull(TestValues.NULL, msg.getPreferredFPS()); } public void testJson() { @@ -90,7 +90,7 @@ public class VideoStreamingCapabilityTests extends TestCase { reference.put(VideoStreamingCapability.KEY_DIAGONAL_SCREEN_SIZE, TestValues.GENERAL_DOUBLE); reference.put(VideoStreamingCapability.KEY_PIXEL_PER_INCH, TestValues.GENERAL_DOUBLE); reference.put(VideoStreamingCapability.KEY_SCALE, TestValues.GENERAL_DOUBLE); - reference.put(VideoStreamingCapability.KEY_PREFERRED_FPS, TestValues.GENERAL_INTEGER); + reference.put(VideoStreamingCapability.KEY_PREFERRED_FPS, TestValues.GENERAL_INTEGER); JSONObject underTest = msg.serializeJSON(); assertEquals(TestValues.MATCH, reference.length(), underTest.length()); @@ -123,14 +123,14 @@ public class VideoStreamingCapabilityTests extends TestCase { } @Test - public void testFormatMethod(){ + public void testFormatMethod() { List<VideoStreamingCapability> additionalCapabilities = msg.getAdditionalVideoStreamingCapabilities(); msg.format(null, false); assertEquals(additionalCapabilities, msg.getAdditionalVideoStreamingCapabilities()); } @Test - public void testFormatWillRemoveSelf(){ + public void testFormatWillRemoveSelf() { List<VideoStreamingCapability> additionalCapabilities = msg.getAdditionalVideoStreamingCapabilities(); additionalCapabilities.add(msg); msg.format(null, false); diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/WindowCapabilityTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/WindowCapabilityTests.java index 0ad5e466d..756791a5a 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/WindowCapabilityTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/WindowCapabilityTests.java @@ -220,7 +220,7 @@ public class WindowCapabilityTests extends TestCase { imageFieldNameListTest.add((ImageFieldName) underTestArray.get(i)); } assertTrue(TestValues.TRUE, imageFieldNameListReference.containsAll(imageFieldNameListTest) && imageFieldNameListTest.containsAll(imageFieldNameListReference)); - } else if (key.equals(WindowCapability.KEY_KEYBOARD_CAPABILITIES)){ + } else if (key.equals(WindowCapability.KEY_KEYBOARD_CAPABILITIES)) { JSONObject jsonReference = JsonUtils.readJsonObjectFromJsonObject(reference, key); JSONObject jsonUnderTest = JsonUtils.readJsonObjectFromJsonObject(underTest, key); diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/AppCapabilityTypeTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/AppCapabilityTypeTests.java index dd1f08080..22db62763 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/AppCapabilityTypeTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/AppCapabilityTypeTests.java @@ -13,7 +13,7 @@ public class AppCapabilityTypeTests extends TestCase { /** * Verifies that the enum values are not null upon valid assignment. */ - public void testValidEnums () { + public void testValidEnums() { String example = "VIDEO_STREAMING"; AppCapabilityType enumVideoStreaming = AppCapabilityType.valueForString(example); @@ -23,7 +23,7 @@ public class AppCapabilityTypeTests extends TestCase { /** * Verifies that an invalid assignment is null. */ - public void testInvalidEnum () { + public void testInvalidEnum() { String example = "VidEOs_Treamin"; try { AppCapabilityType temp = AppCapabilityType.valueForString(example); @@ -37,7 +37,7 @@ public class AppCapabilityTypeTests extends TestCase { /** * Verifies that a null assignment is invalid. */ - public void testNullEnum () { + public void testNullEnum() { String example = null; try { AppCapabilityType temp = AppCapabilityType.valueForString(example); diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/notifications/OnAppCapabilityUpdatedTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/notifications/OnAppCapabilityUpdatedTests.java index eaca5122c..c9717bf97 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/notifications/OnAppCapabilityUpdatedTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/notifications/OnAppCapabilityUpdatedTests.java @@ -19,7 +19,7 @@ import static junit.framework.TestCase.fail; public class OnAppCapabilityUpdatedTests extends BaseRpcTests { @Override - protected RPCMessage createMessage(){ + protected RPCMessage createMessage() { OnAppCapabilityUpdated msg = new OnAppCapabilityUpdated(); msg.setAppCapability(TestValues.GENERAL_APPCAPABILITY); @@ -27,22 +27,22 @@ public class OnAppCapabilityUpdatedTests extends BaseRpcTests { } @Override - protected String getMessageType(){ + protected String getMessageType() { return RPCMessage.KEY_NOTIFICATION; } @Override - protected String getCommandType(){ + protected String getCommandType() { return FunctionID.ON_APP_CAPABILITY_UPDATED.toString(); } @Override - protected JSONObject getExpectedParameters(int sdlVersion){ + protected JSONObject getExpectedParameters(int sdlVersion) { JSONObject result = new JSONObject(); - try{ + try { result.put(OnAppCapabilityUpdated.KEY_APP_CAPABILITY, TestValues.GENERAL_APPCAPABILITY.serializeJSON()); - }catch(JSONException e){ + } catch (JSONException e) { fail(TestValues.JSON_FAIL); } @@ -53,9 +53,9 @@ public class OnAppCapabilityUpdatedTests extends BaseRpcTests { * Tests the expected values of the RPC message. */ @Test - public void testRpcValues () { + public void testRpcValues() { // Test Values - AppCapability appCapability = ( (OnAppCapabilityUpdated) msg ).getAppCapability(); + AppCapability appCapability = ((OnAppCapabilityUpdated) msg).getAppCapability(); // Valid Tests assertEquals(TestValues.MATCH, TestValues.GENERAL_APPCAPABILITY, appCapability); diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java b/android/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java index 3eba6af30..35fc87683 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java @@ -228,9 +228,7 @@ public class VirtualDisplayEncoder { mCaptureThread.stopAsync(); try { mCaptureThread.join(); - } catch(InterruptedException e) { - - } + } catch (InterruptedException e) {} mCaptureThread = null; } if (encoderThread != null) { @@ -253,6 +251,10 @@ public class VirtualDisplayEncoder { inputSurface.release(); inputSurface = null; } + if (mEglCore != null) { + mEglCore.release(); + mEglCore = null; + } } catch (Exception ex) { DebugTool.logError(TAG, "shutDown() failed"); } @@ -264,6 +266,9 @@ public class VirtualDisplayEncoder { * @param Height */ private void setupGLES(int Width, int Height) { + if (mEglCore != null) { + mEglCore.release(); + } mEglCore = new EglCore(null, 0); // This 1x1 offscreen is created just to get the texture name (mTextureId). @@ -328,7 +333,7 @@ public class VirtualDisplayEncoder { mBlit = blit; mWidth = width; mHeight = height; - mFrameIntervalNsec = (long)(1000000000 / fps); + mFrameIntervalNsec = (long) (1000000000 / fps); mStartedCallback = onStarted; } @@ -482,6 +487,27 @@ public class VirtualDisplayEncoder { // Create a MediaCodec encoder and configure it. Get a Surface we can use for recording into. try { mVideoEncoder = MediaCodec.createEncoderByType(videoMimeType); + + int width = streamingParams.getResolution().getResolutionWidth(); + int height = streamingParams.getResolution().getResolutionHeight(); + int frameRate = streamingParams.getFrameRate(); + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + boolean streamSupported = mVideoEncoder.getCodecInfo() + .getCapabilitiesForType(videoMimeType) + .getVideoCapabilities() + .areSizeAndRateSupported(width, height, frameRate); + + if (!streamSupported) { + String errorString = "Video streaming " + width + " by " + height + " at " + + frameRate + "fps is unsupported on this device"; + + DebugTool.logError(TAG, errorString); + + return null; + } + } + mVideoEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); Surface surface = mVideoEncoder.createInputSurface(); //prepared diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java index 866b6e80b..6dc3ce4aa 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java @@ -91,6 +91,19 @@ public class SdlManager extends BaseSdlManager { private VideoStreamManager videoStreamManager; private AudioStreamManager audioStreamManager; + private Handler handler = new Handler(Looper.getMainLooper()); + private Runnable changeRegistrationRunnable = new Runnable() { + @Override + public void run() { + checkLifecycleConfiguration(); + DebugTool.logInfo(TAG, "Retry Change Registration Count: " + changeRegistrationRetry); + } + }; + + public SdlManager() { + DebugTool.logWarning(TAG, "SdlManager must be created with SdlManager.Builder"); + } + /** * Starts up a SdlManager, and calls provided callback called once all BaseSubManagers are done setting up */ @@ -211,14 +224,9 @@ public class SdlManager extends BaseSdlManager { void retryChangeRegistration() { changeRegistrationRetry++; if (changeRegistrationRetry < MAX_RETRY) { - final Handler handler = new Handler(Looper.getMainLooper()); - handler.postDelayed(new Runnable() { - @Override - public void run() { - checkLifecycleConfiguration(); - DebugTool.logInfo(TAG, "Retry Change Registration Count: " + changeRegistrationRetry); - } - }, 3000); + if (handler != null && changeRegistrationRunnable != null) { + handler.postDelayed(changeRegistrationRunnable, 3000); + } } } @@ -229,6 +237,11 @@ public class SdlManager extends BaseSdlManager { @SuppressLint("NewApi") @Override public synchronized void dispose() { + int state = getState(); + if (state == BaseSubManager.SHUTDOWN || state == BaseSubManager.ERROR) { + DebugTool.logInfo(TAG, "SdlManager already disposed"); + return; + } if (this.permissionManager != null) { this.permissionManager.dispose(); } @@ -258,6 +271,15 @@ public class SdlManager extends BaseSdlManager { this.lifecycleManager.stop(); } + if (handler != null) { + if (changeRegistrationRunnable != null) { + handler.removeCallbacks(changeRegistrationRunnable); + changeRegistrationRunnable = null; + } + + handler = null; + } + if (managerListener != null) { managerListener.onDestroy(); managerListener = null; @@ -397,7 +419,7 @@ public class SdlManager extends BaseSdlManager { @Override public void stop() { - lifecycleManager.getInternalInterface(SdlManager.this).start(); + lifecycleManager.getInternalInterface(SdlManager.this).stop(); } @Override @@ -447,7 +469,7 @@ public class SdlManager extends BaseSdlManager { @Override public boolean removeOnRPCNotificationListener(FunctionID notificationId, OnRPCNotificationListener listener) { - return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCNotificationListener(notificationId, listener); + return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCNotificationListener(notificationId, listener); } @Override @@ -457,7 +479,7 @@ public class SdlManager extends BaseSdlManager { @Override public boolean removeOnRPCRequestListener(FunctionID functionID, OnRPCRequestListener listener) { - return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCRequestListener(functionID, listener); + return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCRequestListener(functionID, listener); } @Override @@ -467,29 +489,29 @@ public class SdlManager extends BaseSdlManager { @Override public boolean removeOnRPCListener(FunctionID responseId, OnRPCListener listener) { - return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCListener(responseId, listener); + return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCListener(responseId, listener); } @Override public RegisterAppInterfaceResponse getRegisterAppInterfaceResponse() { - return lifecycleManager.getInternalInterface(SdlManager.this).getRegisterAppInterfaceResponse(); + return lifecycleManager.getInternalInterface(SdlManager.this).getRegisterAppInterfaceResponse(); } @Override public boolean isTransportForServiceAvailable(SessionType serviceType) { - return lifecycleManager.getInternalInterface(SdlManager.this).isTransportForServiceAvailable(serviceType); + return lifecycleManager.getInternalInterface(SdlManager.this).isTransportForServiceAvailable(serviceType); } @NonNull @Override public SdlMsgVersion getSdlMsgVersion() { - return lifecycleManager.getInternalInterface(SdlManager.this).getSdlMsgVersion(); + return lifecycleManager.getInternalInterface(SdlManager.this).getSdlMsgVersion(); } @NonNull @Override public Version getProtocolVersion() { - return lifecycleManager.getInternalInterface(SdlManager.this).getProtocolVersion(); + return lifecycleManager.getInternalInterface(SdlManager.this).getProtocolVersion(); } @Override @@ -504,12 +526,12 @@ public class SdlManager extends BaseSdlManager { @Override public Taskmaster getTaskmaster() { - return lifecycleManager.getInternalInterface(SdlManager.this).getTaskmaster(); + return lifecycleManager.getInternalInterface(SdlManager.this).getTaskmaster(); } @Override public SystemCapabilityManager getSystemCapabilityManager() { - return lifecycleManager.getInternalInterface(SdlManager.this).getSystemCapabilityManager(); + return lifecycleManager.getInternalInterface(SdlManager.this).getSystemCapabilityManager(); } @Override diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java index a0c0d9369..eeb62b158 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java @@ -252,7 +252,7 @@ public class SdlFile implements Cloneable { } /** - * Sets the the name of the static file. Static files comes pre-shipped with the head unit + * Sets the name of the static file. Static files comes pre-shipped with the head unit * * @param staticIcon a StaticIconName enum value representing the name of a static file that comes pre-shipped with the head unit */ @@ -261,7 +261,7 @@ public class SdlFile implements Cloneable { } /** - * Gets the the name of the static file. Static files comes pre-shipped with the head unit + * Gets the name of the static file. Static files comes pre-shipped with the head unit * * @return a StaticIconName enum value representing the name of a static file that comes pre-shipped with the head unit */ 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 6d4fddb3d..bfe73c19f 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 @@ -32,6 +32,8 @@ package com.smartdevicelink.managers.lifecycle; +import static com.smartdevicelink.managers.BaseSubManager.SETTING_UP; + import android.content.Context; import androidx.annotation.RestrictTo; @@ -76,28 +78,33 @@ public class LifecycleManager extends BaseLifecycleManager { void initialize() { super.initialize(); - if (_transportConfig != null && _transportConfig.getTransportType().equals(TransportType.MULTIPLEX)) { - this.session = new SdlSession(sdlSessionListener, (MultiplexTransportConfig) _transportConfig); - } else if (_transportConfig != null && _transportConfig.getTransportType().equals(TransportType.TCP)) { - this.session = new SdlSession(sdlSessionListener, (TCPTransportConfig) _transportConfig); - } else { - DebugTool.logError(TAG, "Unable to create session for transport type"); + synchronized (SESSION_LOCK) { + if (_transportConfig != null && _transportConfig.getTransportType().equals(TransportType.MULTIPLEX)) { + this.session = new SdlSession(sdlSessionListener, (MultiplexTransportConfig) _transportConfig); + } else if (_transportConfig != null && _transportConfig.getTransportType().equals(TransportType.TCP)) { + this.session = new SdlSession(sdlSessionListener, (TCPTransportConfig) _transportConfig); + } else { + DebugTool.logError(TAG, "Unable to create session for transport type"); + } } } @Override void cycle(SdlDisconnectedReason disconnectedReason) { - clean(); - initialize(); + clean(true); if (!SdlDisconnectedReason.LEGACY_BLUETOOTH_MODE_ENABLED.equals(disconnectedReason) && !SdlDisconnectedReason.PRIMARY_TRANSPORT_CYCLE_REQUEST.equals(disconnectedReason)) { //We don't want to alert higher if we are just cycling for legacy bluetooth onClose("Sdl Proxy Cycled", new SdlException("Sdl Proxy Cycled", SdlExceptionCause.SDL_PROXY_CYCLED), disconnectedReason); } - if (session != null) { - try { - session.startSession(); - } catch (SdlException e) { - e.printStackTrace(); + transitionToState(SETTING_UP); + initialize(); + synchronized (SESSION_LOCK) { + if (session != null) { + try { + session.startSession(); + } catch (SdlException e) { + e.printStackTrace(); + } } } } @@ -114,7 +121,7 @@ public class LifecycleManager extends BaseLifecycleManager { return; } - for (TransportRecord record: activeTransports) { + for (TransportRecord record : activeTransports) { if (record.getType() == TransportType.BLUETOOTH) { String address = record.getAddress(); if (address != null && !address.isEmpty()) { @@ -163,16 +170,18 @@ public class LifecycleManager extends BaseLifecycleManager { */ @Override void startVideoService(boolean isEncrypted, VideoStreamingParameters parameters, boolean afterPendingRestart) { - if (session == null) { - DebugTool.logWarning(TAG, "SdlSession is not created yet."); - return; - } - if (!session.getIsConnected()) { - DebugTool.logWarning(TAG, "Connection is not available."); - return; - } + synchronized (SESSION_LOCK) { + if (session == null) { + DebugTool.logWarning(TAG, "SdlSession is not created yet."); + return; + } + if (!session.getIsConnected()) { + DebugTool.logWarning(TAG, "Connection is not available."); + return; + } - session.setDesiredVideoParams(parameters); + session.setDesiredVideoParams(parameters); + } tryStartVideoStream(isEncrypted, parameters, afterPendingRestart); } @@ -185,9 +194,11 @@ public class LifecycleManager extends BaseLifecycleManager { * @param parameters VideoStreamingParameters that are desired. Does not guarantee this is what will be accepted. */ private void tryStartVideoStream(boolean isEncrypted, VideoStreamingParameters parameters, boolean afterPendingRestart) { - if (session == null) { - DebugTool.logWarning(TAG, "SdlSession is not created yet."); - return; + synchronized (SESSION_LOCK) { + if (session == null) { + DebugTool.logWarning(TAG, "SdlSession is not created yet."); + return; + } } if (getProtocolVersion() != null && getProtocolVersion().getMajor() >= 5 && !systemCapabilityManager.isCapabilitySupported(SystemCapabilityType.VIDEO_STREAMING)) { DebugTool.logWarning(TAG, "Module doesn't support video streaming."); @@ -198,17 +209,20 @@ public class LifecycleManager extends BaseLifecycleManager { return; } + synchronized (SESSION_LOCK) { + if (afterPendingRestart || !videoServiceStartResponseReceived || !videoServiceStartResponse //If we haven't started the service before + || (videoServiceStartResponse && isEncrypted && !session.isServiceProtected(SessionType.NAV))) { //Or the service has been started but we'd like to start an encrypted one + if (session != null) { + session.setDesiredVideoParams(parameters); + } + videoServiceStartResponseReceived = false; + videoServiceStartResponse = false; - if (afterPendingRestart || !videoServiceStartResponseReceived || !videoServiceStartResponse //If we haven't started the service before - || (videoServiceStartResponse && isEncrypted && !session.isServiceProtected(SessionType.NAV))) { //Or the service has been started but we'd like to start an encrypted one - session.setDesiredVideoParams(parameters); - - videoServiceStartResponseReceived = false; - videoServiceStartResponse = false; - - addVideoServiceListener(); - session.startService(SessionType.NAV, isEncrypted); - + addVideoServiceListener(); + if (session != null) { + session.startService(SessionType.NAV, isEncrypted); + } + } } } @@ -237,20 +251,27 @@ public class LifecycleManager extends BaseLifecycleManager { videoServiceStartResponse = false; } }; - session.addServiceListener(SessionType.NAV, videoServiceListener); + + synchronized (SESSION_LOCK) { + if (session != null) { + session.addServiceListener(SessionType.NAV, videoServiceListener); + } + } } } @Override void startAudioService(boolean isEncrypted) { - if (session == null) { - DebugTool.logWarning(TAG, "SdlSession is not created yet."); - return; - } - if (!session.getIsConnected()) { - DebugTool.logWarning(TAG, "Connection is not available."); - return; + synchronized (SESSION_LOCK) { + if (session == null) { + DebugTool.logWarning(TAG, "SdlSession is not created yet."); + return; + } + if (!session.getIsConnected()) { + DebugTool.logWarning(TAG, "Connection is not available."); + return; + } + session.startService(SessionType.PCM, isEncrypted); } - session.startService(SessionType.PCM, isEncrypted); } } diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java index 97e8c8bca..3f6cfb4f3 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java @@ -205,7 +205,7 @@ public class LockScreenManager extends BaseSubManager { driverDistStatus = DriverDistractionState.DD_ON.equals(ddState.getState()); mLockscreenWarningMsg = ddState.getLockscreenWarningMessage(); boolean previousDismissibleState = isLockscreenDismissible; - if(ddState.getLockscreenDismissibility() != null ) { + if (ddState.getLockscreenDismissibility() != null) { isLockscreenDismissible = ddState.getLockscreenDismissibility(); } //If the param is null, we assume it stays as the previous value diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java index cce131baa..9874c0d7c 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java @@ -144,22 +144,34 @@ public class VideoStreamManager extends BaseVideoStreamManager { hapticManager = new HapticInterfaceManager(internalInterface); } checkState(); - startEncoder(); - stateMachine.transitionToState(StreamingStateMachine.STARTED); - hasStarted = true; + boolean encoderStarted = startEncoder(); + if (encoderStarted) { + stateMachine.transitionToState(StreamingStateMachine.STARTED); + hasStarted = true; + } else { + DebugTool.logError(TAG, "Error starting video encoder"); + stateMachine.transitionToState(StreamingStateMachine.ERROR); + withPendingRestart = false; + if (session != null) { + session.endService(SessionType.NAV); + } + } } } @Override public void onServiceEnded(SdlSession session, SessionType type) { if (SessionType.NAV.equals(type)) { - if (remoteDisplay !=null){ - stopStreaming(withPendingRestart); + if (remoteDisplay != null) { + if (withPendingRestart && isHMIStateVideoStreamCapable(currentOnHMIStatus)) { + stopStreaming(withPendingRestart); + } else { + stopStreaming(); + } } stateMachine.transitionToState(StreamingStateMachine.NONE); transitionToState(SETTING_UP); - - if (withPendingRestart){ + if (withPendingRestart && isHMIStateVideoStreamCapable(currentOnHMIStatus)) { VideoStreamManager manager = VideoStreamManager.this; manager.internalInterface.startVideoService(manager.getLastCachedStreamingParameters(), manager.isEncrypted, withPendingRestart); } @@ -198,6 +210,10 @@ public class VideoStreamManager extends BaseVideoStreamManager { if (hasStarted && (isHMIStateVideoStreamCapable(prevOnHMIStatus)) && (!isHMIStateVideoStreamCapable(currentOnHMIStatus))) { stopVideoStream(); } + if (withPendingRestart && hasStarted && (!isHMIStateVideoStreamCapable(prevOnHMIStatus)) && (isHMIStateVideoStreamCapable(currentOnHMIStatus))) { + VideoStreamManager manager = VideoStreamManager.this; + manager.internalInterface.startVideoService(manager.getLastCachedStreamingParameters(), manager.isEncrypted, withPendingRestart); + } } } }; @@ -221,7 +237,7 @@ public class VideoStreamManager extends BaseVideoStreamManager { public void onCapabilityRetrieved(Object capability) { VideoStreamingParameters params = (parameters == null) ? new VideoStreamingParameters() : new VideoStreamingParameters(parameters); - VideoStreamingCapability castedCapability = ((VideoStreamingCapability)capability); + VideoStreamingCapability castedCapability = ((VideoStreamingCapability) capability); // means only scale received if (castedCapability.getPreferredResolution() == null && @@ -297,12 +313,12 @@ public class VideoStreamManager extends BaseVideoStreamManager { @Override public void onCapabilityRetrieved(Object capability) { VideoStreamingParameters params = new VideoStreamingParameters(); - VideoStreamingCapability castedCapability = ((VideoStreamingCapability)capability); + VideoStreamingCapability castedCapability = ((VideoStreamingCapability) capability); VideoStreamManager.this.originalCapability = castedCapability; params.update(castedCapability, vehicleMake);//Streaming parameters are ready time to stream VideoStreamManager.this.parameters = params; checkState(); - } + } @Override public void onError(String info) { @@ -351,12 +367,13 @@ public class VideoStreamManager extends BaseVideoStreamManager { */ public void startRemoteDisplayStream(Context context, Class<? extends SdlRemoteDisplay> remoteDisplayClass, VideoStreamingParameters parameters, final boolean encrypted, VideoStreamingRange supportedLandscapeStreamingRange, VideoStreamingRange supportedPortraitStreamingRange) { configureGlobalParameters(context, remoteDisplayClass, encrypted, supportedPortraitStreamingRange, supportedLandscapeStreamingRange); - if(majorProtocolVersion >= 5 && !internalInterface.getSystemCapabilityManager().isCapabilitySupported(SystemCapabilityType.VIDEO_STREAMING)){ + if (majorProtocolVersion >= 5 && !internalInterface.getSystemCapabilityManager().isCapabilitySupported(SystemCapabilityType.VIDEO_STREAMING)) { stateMachine.transitionToState(StreamingStateMachine.ERROR); return; } processCapabilitiesWithPendingStart(encrypted, parameters); } + /** * Starts streaming a remote display to the module if there is a connected session. This method of streaming requires the device to be on API level 19 or higher * @@ -368,7 +385,7 @@ public class VideoStreamManager extends BaseVideoStreamManager { * @param encrypted a flag of if the stream should be encrypted. Only set if you have a supplied encryption library that the module can understand. */ @Deprecated - public void startRemoteDisplayStream(Context context, Class<? extends SdlRemoteDisplay> remoteDisplayClass, VideoStreamingParameters parameters, final boolean encrypted){ + public void startRemoteDisplayStream(Context context, Class<? extends SdlRemoteDisplay> remoteDisplayClass, VideoStreamingParameters parameters, final boolean encrypted) { configureGlobalParameters(context, remoteDisplayClass, encrypted, null, null); boolean isCapabilitySupported = internalInterface.getSystemCapabilityManager() != null && internalInterface.getSystemCapabilityManager().isCapabilitySupported(SystemCapabilityType.VIDEO_STREAMING); if (majorProtocolVersion >= 5 && !isCapabilitySupported) { @@ -472,7 +489,7 @@ public class VideoStreamManager extends BaseVideoStreamManager { /** * Initializes and starts the virtual display encoder and creates the remote display */ - private void startEncoder() { + private boolean startEncoder() { try { if (remoteDisplay != null) { remoteDisplay.resizeView(parameters.getResolution().getResolutionWidth(), parameters.getResolution().getResolutionHeight()); @@ -489,7 +506,9 @@ public class VideoStreamManager extends BaseVideoStreamManager { } catch (Exception e) { stateMachine.transitionToState(StreamingStateMachine.ERROR); e.printStackTrace(); + return false; } + return true; } /** @@ -577,7 +596,7 @@ public class VideoStreamManager extends BaseVideoStreamManager { return (stateMachine.getState() == StreamingStateMachine.READY) || (stateMachine.getState() == StreamingStateMachine.STARTED) || (stateMachine.getState() == StreamingStateMachine.STOPPED); - } + } /** * Check if video is currently streaming and visible @@ -671,8 +690,8 @@ public class VideoStreamManager extends BaseVideoStreamManager { //A good time to refresh spatial data DisplayMetrics displayMetrics = new DisplayMetrics(); VideoStreamManager.this.remoteDisplay.getDisplay().getMetrics(displayMetrics); - displayMetrics.widthPixels = (int) (parameters.getResolution().getResolutionWidth() * parameters.getScale()); - displayMetrics.heightPixels = (int) (parameters.getResolution().getResolutionHeight() * parameters.getScale()); + displayMetrics.widthPixels = (int) (parameters.getResolution().getResolutionWidth() * parameters.getScale()); + displayMetrics.heightPixels = (int) (parameters.getResolution().getResolutionHeight() * parameters.getScale()); createTouchScalar(parameters.getResolution(), displayMetrics); if (hapticManager != null) { remoteDisplay.getMainView().post(new Runnable() { @@ -804,16 +823,16 @@ public class VideoStreamManager extends BaseVideoStreamManager { return parameters; } - private List<VideoStreamingCapability> getSupportedCapabilities(VideoStreamingCapability rootCapability){ + private List<VideoStreamingCapability> getSupportedCapabilities(VideoStreamingCapability rootCapability) { List<VideoStreamingCapability> validCapabilities = new ArrayList<>(); - if (rootCapability == null){ + if (rootCapability == null) { return null; } List<VideoStreamingCapability> allCapabilities = new ArrayList<>(); List<VideoStreamingCapability> additionalCapabilities = rootCapability.getAdditionalVideoStreamingCapabilities(); - if (additionalCapabilities != null){ + if (additionalCapabilities != null) { allCapabilities.addAll(additionalCapabilities); } rootCapability.setAdditionalVideoStreamingCapabilities(null); @@ -847,7 +866,7 @@ public class VideoStreamManager extends BaseVideoStreamManager { return validCapabilities; } - private Boolean inRange(VideoStreamingCapability capability, VideoStreamingRange range){ + private Boolean inRange(VideoStreamingCapability capability, VideoStreamingRange range) { if (capability == null) { return false; } @@ -855,7 +874,7 @@ public class VideoStreamManager extends BaseVideoStreamManager { return true; } - if (isZeroRange(range)){ + if (isZeroRange(range)) { return false; } @@ -868,7 +887,7 @@ public class VideoStreamManager extends BaseVideoStreamManager { ImageResolution resolution = capability.getPreferredResolution(); if (resolution != null) { Double currentAspectRatio = normalizeAspectRatio(resolution); - if (!range.isAspectRatioInRange(currentAspectRatio)){ + if (!range.isAspectRatioInRange(currentAspectRatio)) { return false; } } @@ -892,9 +911,9 @@ public class VideoStreamManager extends BaseVideoStreamManager { if (width <= 0.0 || height <= 0.0) { return 0.0; } else if (width < height) { - return height/width; + return height / width; } else if (width > height) { - return width/height; + return width / height; } else { return 1.0; } @@ -910,14 +929,14 @@ public class VideoStreamManager extends BaseVideoStreamManager { } } - private Boolean isZeroRange(VideoStreamingRange range){ - if (range == null || range.getMaximumResolution() == null || range.getMinimumResolution() == null){ + private Boolean isZeroRange(VideoStreamingRange range) { + if (range == null || range.getMaximumResolution() == null || range.getMinimumResolution() == null) { return false; } return isZeroResolution(range.getMaximumResolution()) && isZeroResolution(range.getMinimumResolution()); } - private boolean isZeroResolution(Resolution resolution){ + private boolean isZeroResolution(Resolution resolution) { if (resolution == null) { return false; } diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/resolution/VideoStreamingRange.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/resolution/VideoStreamingRange.java index f94220eef..cac0d62d9 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/resolution/VideoStreamingRange.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/resolution/VideoStreamingRange.java @@ -108,7 +108,7 @@ public class VideoStreamingRange { if (maximumAspectRatio == null && minimumAspectRatio == null) { return true; } - if (aspectRatio == null){ + if (aspectRatio == null) { return false; } diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java index 31cea21c8..2eea57b98 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java @@ -117,7 +117,7 @@ public class MultiplexBluetoothTransport extends MultiplexBaseTransport { * A method to retrieve the currently connected bluetooth device * @return the connected bluetooth device if connected, null otherwise. */ - public BluetoothDevice getConnectedDevice(){ + public BluetoothDevice getConnectedDevice() { return connectedDevice; } 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 588b905cc..7bed1b482 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 @@ -66,6 +66,7 @@ import java.util.Locale; import java.util.Vector; import java.util.concurrent.ConcurrentLinkedQueue; +import static android.Manifest.permission.BLUETOOTH_CONNECT; import static com.smartdevicelink.transport.TransportConstants.FOREGROUND_EXTRA; public abstract class SdlBroadcastReceiver extends BroadcastReceiver { @@ -73,6 +74,7 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "Sdl Broadcast Receiver"; protected static final String SDL_ROUTER_SERVICE_CLASS_NAME = "sdlrouterservice"; + protected static final int ANDROID_12_ROUTER_SERVICE_VERSION = 16; public static final String LOCAL_ROUTER_SERVICE_EXTRA = "router_service"; public static final String LOCAL_ROUTER_SERVICE_DID_START_OWN = "did_start"; @@ -90,6 +92,7 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { private static Thread.UncaughtExceptionHandler foregroundExceptionHandler = null; private static final Object DEVICE_LISTENER_LOCK = new Object(); private static SdlDeviceListener sdlDeviceListener; + private static String serviceName = null; public int getRouterServiceVersion() { return SdlRouterService.ROUTER_SERVICE_VERSION_NUMBER; @@ -98,6 +101,7 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { @Override @CallSuper public void onReceive(Context context, Intent intent) { + //Log.i(TAG, "Sdl Receiver Activated"); final String action = intent.getAction(); if (action == null) { @@ -132,6 +136,10 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); } + if (serviceName == null) { + serviceName = getSdlServiceName(); + } + boolean didStart = false; if (localRouterClass == null) { localRouterClass = defineLocalSdlRouterClass(); @@ -296,6 +304,18 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { String routerServicePackage = null; if (sdlAppInfoList != null && !sdlAppInfoList.isEmpty() && sdlAppInfoList.get(0).getRouterServiceComponentName() != null) { routerServicePackage = sdlAppInfoList.get(0).getRouterServiceComponentName().getPackageName(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + // If all apps have a RS newer than the Android 12 update, chosen app does not have BT Connect permissions, and more than 1 sdl app is installed + if (!isPreAndroid12RSOnDevice(sdlAppInfoList) && !AndroidTools.isPermissionGranted(BLUETOOTH_CONNECT, context, routerServicePackage) && sdlAppInfoList.size() > 1) { + for (SdlAppInfo appInfo : sdlAppInfoList) { + if (AndroidTools.isPermissionGranted(BLUETOOTH_CONNECT, context, appInfo.getRouterServiceComponentName().getPackageName())) { + //If this app in the list has BT Connect permissions, we want to use that apps RS + routerServicePackage = appInfo.getRouterServiceComponentName().getPackageName(); + break; + } + } + } + } } DebugTool.logInfo(TAG, ": This app's package: " + myPackage); DebugTool.logInfo(TAG, ": Router service app's package: " + routerServicePackage); @@ -303,7 +323,7 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { //If the device is not null the listener should start as well as the //case where this app was installed after BT connected and is the //only SDL app installed on the device. (Rare corner case) - if(device != null || sdlAppInfoList.size() == 1) { + if (device != null || sdlAppInfoList.size() == 1) { SdlDeviceListener sdlDeviceListener = getSdlDeviceListener(context, device); if (!sdlDeviceListener.isRunning()) { sdlDeviceListener.start(); @@ -362,13 +382,16 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { } /** - * This method will set a new UncaughtExceptionHandler for the current thread. The only - * purpose of the custom UncaughtExceptionHandler is to catch the rare occurrence that the - * SdlRouterService can't be started fast enough by the system after calling - * startForegroundService so the onCreate method doesn't get called before the foreground promise - * timer expires. The new UncaughtExceptionHandler will catch that specific exception and tell the - * main looper to continue forward. This still leaves the SdlRouterService killed, but prevents - * an ANR to the app that makes the startForegroundService call. + * This method will set a new UncaughtExceptionHandler for the current thread. + * There are two exceptions we want to catch here. The first exception is the rare + * occurrence that the SdlRouterService can't be started fast enough by the system after calling + * startForegroundService so the onCreate method doesn't get called before the foreground + * promise timer expires. The second is for the instance where the developers "SdlService" class + * can't be started fast enough by the system after calling startForegroundService OR the app + * is unable to start the "SdlService" class because the developer did not export the service + * in the manifest. The new UncaughtExceptionHandler will catch these specific exception and + * tell the main looper to continue forward. This still leaves the respective Service killed, + * but prevents an ANR to the app that makes the startForegroundService call. */ static protected void setForegroundExceptionHandler() { final Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); @@ -378,10 +401,9 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { public void uncaughtException(Thread t, Throwable e) { if (e != null && e instanceof AndroidRuntimeException - && "android.app.RemoteServiceException".equals(e.getClass().getName()) //android.app.RemoteServiceException is a private class + && ("android.app.RemoteServiceException".equals(e.getClass().getName()) || "android.app.ForegroundServiceDidNotStartInTimeException".equals(e.getClass().getName())) //android.app.RemoteServiceException is a private class && e.getMessage() != null - && e.getMessage().contains("SdlRouterService")) { - + && (e.getMessage().contains("SdlRouterService")) || e.getMessage().contains(serviceName)) { DebugTool.logInfo(TAG, "Handling failed startForegroundService call"); Looper.loop(); } else if (defaultUncaughtExceptionHandler != null) { //No other exception should be handled @@ -598,6 +620,18 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { final List<SdlAppInfo> sdlAppInfoList = AndroidTools.querySdlAppInfo(context, new SdlAppInfo.BestRouterComparator(), vehicleType); if (sdlAppInfoList != null && !sdlAppInfoList.isEmpty()) { ComponentName routerService = sdlAppInfoList.get(0).getRouterServiceComponentName(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + // If all apps have a RS newer than the Android 12 update, chosen app does not have BT Connect permissions, and more than 1 sdl app is installed + if (!isPreAndroid12RSOnDevice(sdlAppInfoList) && !AndroidTools.isPermissionGranted(BLUETOOTH_CONNECT, context, routerService.getPackageName()) && sdlAppInfoList.size() > 1) { + for (SdlAppInfo appInfo : sdlAppInfoList) { + if (AndroidTools.isPermissionGranted(BLUETOOTH_CONNECT, context, appInfo.getRouterServiceComponentName().getPackageName())) { + routerService = appInfo.getRouterServiceComponentName(); + //If this app in the list has BT Connect permissions, we want to use that apps RS + break; + } + } + } + } startRouterService(context, routerService, false, bluetoothDevice, true, vehicleType); } } @@ -634,6 +668,16 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { } } + private static boolean isPreAndroid12RSOnDevice(List<SdlAppInfo> sdlAppInfoList) { + for (SdlAppInfo appInfo : sdlAppInfoList) { + //If an installed app RS version is older than Android 12 update version (16) + if (appInfo.getRouterServiceVersion() < ANDROID_12_ROUTER_SERVICE_VERSION) { + return true; + } + } + return false; + } + /** * We need to define this for local copy of the Sdl Router Service class. * It will be the main point of connection for Sdl enabled apps @@ -656,6 +700,16 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { */ public abstract void onSdlEnabled(Context context, Intent intent); + + /** + * The developer can override this method to return the name of the class that manages their + * SdlService. This method is used to ensure the SdlBroadcastReceivers exception catcher catches + * the correct exception that may be thrown by the app trying to start their SdlService. If this + * exception is not caught the user may experience an ANR for that app. + */ + public String getSdlServiceName() { + return "SdlService"; + } //public abstract void onSdlDisabled(Context context); //Removing for now until we're able to abstract from developer diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java index 9d24ddfec..04c9a2b0e 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java @@ -70,6 +70,7 @@ import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.os.RemoteException; +import android.provider.Settings; import android.util.AndroidRuntimeException; import android.util.SparseArray; import android.util.SparseIntArray; @@ -119,6 +120,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_SCAN; import static com.smartdevicelink.transport.TransportConstants.CONNECTED_DEVICE_STRING_EXTRA_NAME; import static com.smartdevicelink.transport.TransportConstants.FOREGROUND_EXTRA; import static com.smartdevicelink.transport.TransportConstants.FORMED_PACKET_EXTRA_NAME; @@ -142,7 +145,7 @@ public class SdlRouterService extends Service { /** * <b> NOTE: DO NOT MODIFY THIS UNLESS YOU KNOW WHAT YOU'RE DOING.</b> */ - protected static final int ROUTER_SERVICE_VERSION_NUMBER = 15; + protected static final int ROUTER_SERVICE_VERSION_NUMBER = 16; private static final String ROUTER_SERVICE_PROCESS = "com.smartdevicelink.router"; @@ -202,6 +205,7 @@ public class SdlRouterService extends Service { private boolean wrongProcess = false; private boolean initPassed = false; private boolean hasCalledStartForeground = false; + private boolean hasConnectedBefore = false; boolean firstStart = true; public static HashMap<String, RegisteredApp> registeredApps; @@ -215,6 +219,10 @@ public class SdlRouterService extends Service { private boolean startSequenceComplete = false; private VehicleType receivedVehicleType; + private boolean waitingForBTRuntimePermissions = false; + private Handler btPermissionsHandler; + private Runnable btPermissionsRunnable; + private final static int BT_PERMISSIONS_CHECK_FREQUENCY = 1000; private ExecutorService packetExecutor = null; ConcurrentHashMap<TransportType, PacketWriteTaskMaster> packetWriteTaskMasterMap = null; @@ -365,6 +373,11 @@ public class SdlRouterService extends Service { switch (msg.what) { case TransportConstants.ROUTER_REQUEST_BT_CLIENT_CONNECT: + //Starting with Android 12 this use case will require the BLUETOOTH_SCAN PERMISSION + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !AndroidTools.isPermissionGranted(BLUETOOTH_SCAN, service.getApplicationContext(), service.getPackageName())) { + DebugTool.logError(TAG, "BLUETOOTH_SCAN Permissions not granted for this app"); + break; + } if (receivedBundle.getBoolean(TransportConstants.CONNECT_AS_CLIENT_BOOLEAN_EXTRA, false) && !connectAsClient) { //We check this flag to make sure we don't try to connect over and over again. On D/C we should set to false //Log.d(TAG,"Attempting to connect as bt client"); @@ -1085,7 +1098,7 @@ public class SdlRouterService extends Service { * * @return true if this service is set up correctly */ - private boolean initCheck() { + private boolean initCheck(boolean isConnectedOverUSB) { if (!processCheck()) { DebugTool.logError(TAG, "Not using correct process. Shutting down"); wrongProcess = true; @@ -1095,6 +1108,14 @@ public class SdlRouterService extends Service { DebugTool.logError(TAG, "Bluetooth Permission is not granted. Shutting down"); return false; } + + // If Android 12 or newer make sure we have BLUETOOTH_CONNECT Runtime permission + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !AndroidTools.isPermissionGranted(BLUETOOTH_CONNECT, this, this.getPackageName())) { + if (!isConnectedOverUSB) { //If BLUETOOTH_CONNECT permission is not granted We want to make sure we are connected over USB + return false; + } + } + if (!AndroidTools.isServiceExported(this, new ComponentName(this, this.getClass()))) { //We want to check to see if our service is actually exported DebugTool.logError(TAG, "Service isn't exported. Shutting down"); return false; @@ -1256,10 +1277,14 @@ public class SdlRouterService extends Service { (HashMap<String, Object>) intent.getSerializableExtra(TransportConstants.VEHICLE_INFO_EXTRA) ); } + boolean isConnectedOverUSB = false; + if (intent != null && intent.hasExtra(TransportConstants.CONNECTION_TYPE_EXTRA)) { + isConnectedOverUSB = TransportConstants.AOA_USB.equalsIgnoreCase(intent.getStringExtra(TransportConstants.CONNECTION_TYPE_EXTRA)); + } // Only trusting the first intent received to start the RouterService and run initial checks to avoid a case where an app could send incorrect data after the spp connection has started. if (firstStart) { firstStart = false; - if (!initCheck()) { // Run checks on process and permissions + if (!initCheck(isConnectedOverUSB)) { // Run checks on process and permissions deployNextRouterService(); closeSelf(); return START_REDELIVER_INTENT; @@ -1296,6 +1321,7 @@ public class SdlRouterService extends Service { } boolean confirmedDevice = intent.getBooleanExtra(TransportConstants.CONFIRMED_SDL_DEVICE, false); int timeout = getNotificationTimeout(address, confirmedDevice); + hasConnectedBefore = hasSDLConnected(address); enterForeground("Waiting for connection...", timeout, false); resetForegroundTimeOut(timeout); @@ -1337,6 +1363,10 @@ public class SdlRouterService extends Service { altTransportTimerHandler = null; } + if (btPermissionsHandler != null && btPermissionsRunnable != null) { + btPermissionsHandler.removeCallbacks(btPermissionsRunnable); + } + DebugTool.logWarning(TAG, "Sdl Router Service Destroyed"); closing = true; //No need for this Broadcast Receiver anymore @@ -1499,6 +1529,10 @@ public class SdlRouterService extends Service { builder = new Notification.Builder(this, SDL_NOTIFICATION_CHANNEL_ID); } + if (hasConnectedBefore && android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + builder.setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE); + } + if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)) { //If we are in debug mode, include what app has the router service open ComponentName name = new ComponentName(this, this.getClass()); builder.setContentTitle("SDL: " + name.getPackageName()); @@ -1521,7 +1555,8 @@ public class SdlRouterService extends Service { // Create an intent that will be fired when the user clicks the notification. Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(SDL_NOTIFICATION_FAQS_PAGE)); - PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0); + int flag = android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_IMMUTABLE : 0; + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, flag); builder.setContentIntent(pendingIntent); if (chronometerLength > (FOREGROUND_TIMEOUT / 1000) && android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { @@ -1569,12 +1604,22 @@ public class SdlRouterService extends Service { private void safeStartForeground(int id, Notification notification) { try { if (notification == null) { - //Try the NotificationCompat this time in case there was a previous error - NotificationCompat.Builder builder = - new NotificationCompat.Builder(this, SDL_NOTIFICATION_CHANNEL_ID) + if (hasConnectedBefore && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { + Notification.Builder builder = + new Notification.Builder(this, SDL_NOTIFICATION_CHANNEL_ID) .setContentTitle("SmartDeviceLink") - .setContentText("Service Running"); - notification = builder.build(); + .setContentText("Service Running") + .setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE); + notification = builder.build(); + } else { + //Try the NotificationCompat this time in case there was a previous error + NotificationCompat.Builder builder = + new NotificationCompat.Builder(this, SDL_NOTIFICATION_CHANNEL_ID) + .setContentTitle("SmartDeviceLink") + .setContentText("Service Running"); + + notification = builder.build(); + } } startForeground(id, notification); DebugTool.logInfo(TAG, "Entered the foreground - " + System.currentTimeMillis()); @@ -1588,9 +1633,9 @@ public class SdlRouterService extends Service { if (isForeground && !isPrimaryTransportConnected()) { //Ensure that the service is in the foreground and no longer connected to a transport DebugTool.logInfo(TAG, "SdlRouterService to exit foreground"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - this.stopForeground(Service.STOP_FOREGROUND_DETACH); + this.stopForeground(Service.STOP_FOREGROUND_REMOVE); } else { - stopForeground(false); + stopForeground(true); } NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (notificationManager != null) { @@ -1684,6 +1729,9 @@ public class SdlRouterService extends Service { */ @SuppressWarnings("MissingPermission") private boolean bluetoothAvailable() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !AndroidTools.isPermissionGranted(BLUETOOTH_CONNECT, SdlRouterService.this, SdlRouterService.this.getPackageName())) { + return false; + } try { return (!(BluetoothAdapter.getDefaultAdapter() == null) && BluetoothAdapter.getDefaultAdapter().isEnabled()); } catch (NullPointerException e) { // only for BluetoothAdapter.getDefaultAdapter().isEnabled() call @@ -1710,7 +1758,7 @@ public class SdlRouterService extends Service { } else if (intent != null && TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT.equals(intent.getAction())) { DebugTool.logInfo(TAG, "Received start intent with alt transport request."); - startAltTransportTimer(); + startAltTransportTimer(); //This timer is started to allow the router service to stay open while it waits for the USB transfer to take place return true; } else if (!bluetoothAvailable()) {//If bluetooth isn't on...there's nothing to see here //Bluetooth is off, we should shut down @@ -1732,8 +1780,8 @@ public class SdlRouterService extends Service { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !hasCalledStartForeground) { //This must be called before stopping self safeStartForeground(FOREGROUND_SERVICE_ID, null); - exitForeground(); } + exitForeground(); if (getBaseContext() != null) { stopSelf(); @@ -1745,6 +1793,11 @@ public class SdlRouterService extends Service { } private synchronized void initBluetoothSerialService() { + if (waitingForBTRuntimePermissions) { + DebugTool.logWarning(TAG, "This app has not been granted the BLUETOOTH_CONNECT runtime permission"); + return; + } + if (legacyModeEnabled) { DebugTool.logInfo(TAG, "Not starting own bluetooth during legacy mode"); return; @@ -1801,6 +1854,17 @@ public class SdlRouterService extends Service { startService.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + //Starting in Android 12 we need to start services from a foreground context + //To enable developers to be able to start their SdlService from the "background" + //we will attach a pendingIntent as an extra to the intent + //the developer can use this pendingIntent to start their SdlService from the context of + //the active RouterService + Intent pending = new Intent(); + PendingIntent pendingIntent = PendingIntent.getForegroundService(this, (int) System.currentTimeMillis(), pending, PendingIntent.FLAG_MUTABLE | Intent.FILL_IN_COMPONENT); + startService.putExtra(TransportConstants.PENDING_INTENT_EXTRA, pendingIntent); + } + AndroidTools.sendExplicitBroadcast(getApplicationContext(), startService, null); //HARDWARE_CONNECTED @@ -1808,6 +1872,31 @@ public class SdlRouterService extends Service { //If we have clients notifyClients(createHardwareConnectedMessage(record)); } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && TransportType.USB.equals(record.getType()) && !AndroidTools.isPermissionGranted(BLUETOOTH_CONNECT, SdlRouterService.this, SdlRouterService.this.getPackageName())) { + //Delay starting bluetoothTransport when we are connected over USB and the app does not have the BLUETOOTH_CONNECT permissions + waitingForBTRuntimePermissions = true; + btPermissionsHandler = new Handler(Looper.myLooper()); + //Continuously Check for the BLUETOOTH_CONNECT Permission + btPermissionsRunnable = new Runnable() { + @Override + public void run() { + if (!AndroidTools.isPermissionGranted(BLUETOOTH_CONNECT, SdlRouterService.this, SdlRouterService.this.getPackageName())) { + btPermissionsHandler.postDelayed(btPermissionsRunnable, BT_PERMISSIONS_CHECK_FREQUENCY); + } else { + waitingForBTRuntimePermissions = false; + initBluetoothSerialService(); + final NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if(notificationManager != null) { + notificationManager.cancel("SDL", TransportConstants.SDL_ERROR_NOTIFICATION_CHANNEL_ID_INT); + } + } + } + }; + btPermissionsHandler.postDelayed(btPermissionsRunnable, BT_PERMISSIONS_CHECK_FREQUENCY); + //Present Notification to take user to permissions page for the app + showBTPermissionsNotification(); + } } private Message createHardwareConnectedMessage(final TransportRecord record) { @@ -1891,6 +1980,12 @@ public class SdlRouterService extends Service { usbSessionMap.clear(); } } + //In case the USB connection has ended before the timer expired, we should stop it + if (altTransportTimerHandler != null && altTransportTimerRunnable != null) { + altTransportTimerHandler.removeCallbacks(altTransportTimerRunnable); + altTransportTimerHandler = null; + altTransportTimerRunnable = null; + } break; case TCP: if (tcpTransport != null) { @@ -1942,7 +2037,7 @@ public class SdlRouterService extends Service { bluetoothTransport.setStateManually(MultiplexBluetoothTransport.STATE_NONE); bluetoothTransport = null; } - if (errorBundle != null && errorBundle.getByte(MultiplexBaseTransport.ERROR_REASON_KEY) == MultiplexBaseTransport.REASON_SPP_ERROR) { + if (errorBundle != null && errorBundle.getByte(MultiplexBaseTransport.ERROR_REASON_KEY) == MultiplexBaseTransport.REASON_SPP_ERROR && bluetoothAvailable()) { notifySppError(); } break; @@ -2467,7 +2562,7 @@ public class SdlRouterService extends Service { /** * Checks to see if a device address has connected to SDL before. * - * @param address the mac address of the device in quesiton + * @param address the mac address of the device in question * @return if this is the first status check of this device */ protected boolean isFirstStatusCheck(String address) { @@ -2478,7 +2573,7 @@ public class SdlRouterService extends Service { /** * Checks to see if a device address has connected to SDL before. * - * @param address the mac address of the device in quesiton + * @param address the mac address of the device in question * @return if an SDL connection has ever been established with this device */ protected boolean hasSDLConnected(String address) { @@ -2542,6 +2637,11 @@ public class SdlRouterService extends Service { if (Looper.myLooper() == null) { Looper.prepare(); } + + if (altTransportTimerHandler != null && altTransportTimerRunnable != null) { + altTransportTimerHandler.removeCallbacks(altTransportTimerRunnable); + } + altTransportTimerHandler = new Handler(Looper.myLooper()); altTransportTimerRunnable = new Runnable() { public void run() { @@ -2743,8 +2843,8 @@ public class SdlRouterService extends Service { } /* **************************************************************************************************************************************** - // *********************************************************** LEGACY **************************************************************** - //*****************************************************************************************************************************************/ + // *********************************************************** LEGACY **************************************************************** + //*****************************************************************************************************************************************/ private boolean legacyModeEnabled = false; private void enableLegacyMode(boolean enable) { @@ -2761,9 +2861,9 @@ public class SdlRouterService extends Service { } - /* **************************************************************************************************************************************** - // *********************************************************** UTILITY **************************************************************** - //*****************************************************************************************************************************************/ + /* **************************************************************************************************************************************** + // *********************************************************** UTILITY **************************************************************** + //*****************************************************************************************************************************************/ private void debugPacket(byte[] bytes) { //DEBUG @@ -2942,9 +3042,9 @@ public class SdlRouterService extends Service { pingIntent = null; } - /* **************************************************************************************************************************************** - // ********************************************************** TINY CLASSES ************************************************************ - //*****************************************************************************************************************************************/ + /* **************************************************************************************************************************************** + // ********************************************************** TINY CLASSES ************************************************************ + //*****************************************************************************************************************************************/ /** * This class enables us to compare two router services @@ -2973,7 +3073,7 @@ public class SdlRouterService extends Service { /** * Check if input is newer than this version * - * @param service a reference to another possible router service that is in quesiton + * @param service a reference to another possible router service that is in question * @return if the supplied service is newer than this one */ public boolean isNewer(@SuppressWarnings("deprecation") LocalRouterService service) { @@ -3138,9 +3238,9 @@ public class SdlRouterService extends Service { return appId; } - /*public long getAppId() { - return appId; - }*/ + /*public long getAppId() { + return appId; + }*/ /** * This is a convenience variable and may not be used or useful in different protocols @@ -3805,6 +3905,10 @@ public class SdlRouterService extends Service { } else { builder = new Notification.Builder(getApplicationContext(), TransportConstants.SDL_ERROR_NOTIFICATION_CHANNEL_ID); } + + if (hasConnectedBefore && android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + builder.setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE); + } ComponentName name = new ComponentName(this, this.getClass()); if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)) { //If we are in debug mode, include what app has the router service open builder.setContentTitle("SDL: " + name.getPackageName()); @@ -3843,4 +3947,59 @@ public class SdlRouterService extends Service { DebugTool.logError(TAG, "notifySppError: Unable to retrieve notification Manager service"); } } + + private void showBTPermissionsNotification() { + Notification.Builder builder; + if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + builder = new Notification.Builder(getApplicationContext()); + } else { + builder = new Notification.Builder(getApplicationContext(), TransportConstants.SDL_ERROR_NOTIFICATION_CHANNEL_ID); + } + + ComponentName name = new ComponentName(this, this.getClass()); + if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)) { //If we are in debug mode, include what app has the router service open + builder.setContentTitle("SDL: " + name.getPackageName()); + } else { + builder.setContentTitle(getString(R.string.notification_title)); + } + builder.setTicker(getString(R.string.sdl_error_notification_channel_name)); + builder.setContentText(getString(R.string.allow_bluetooth_permissions)); + + //We should use icon from library resources if available + int trayId = getResources().getIdentifier("sdl_tray_icon", "drawable", getPackageName()); + + builder.setSmallIcon(trayId); + Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.spp_error); + builder.setLargeIcon(icon); + + builder.setOngoing(false); + builder.setAutoCancel(true); + + // Create an intent that will be fired when the user clicks the notification. + Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + Uri uri = Uri.fromParts("package", getPackageName(), null); + intent.setData(uri); + int flag = android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_IMMUTABLE : 0; + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, flag); + builder.setContentIntent(pendingIntent); + + final String tag = "SDL"; + //Now we need to add a notification channel + final NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager != null) { + notificationManager.cancel(tag, TransportConstants.SDL_ERROR_NOTIFICATION_CHANNEL_ID_INT); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel notificationChannel = new NotificationChannel(TransportConstants.SDL_ERROR_NOTIFICATION_CHANNEL_ID, getString(R.string.sdl_error_notification_channel_name), NotificationManager.IMPORTANCE_HIGH); + notificationChannel.enableLights(true); + notificationChannel.enableVibration(true); + notificationChannel.setShowBadge(false); + notificationManager.createNotificationChannel(notificationChannel); + builder.setChannelId(notificationChannel.getId()); + } + Notification notification = builder.build(); + notificationManager.notify(tag, TransportConstants.SDL_ERROR_NOTIFICATION_CHANNEL_ID_INT, notification); + } else { + DebugTool.logError(TAG, "Unable to retrieve notification Manager service"); + } + } } 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 8e54de9a1..22ee5d1db 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 @@ -167,6 +167,7 @@ public class USBAccessoryAttachmentActivity extends Activity { return;
}
serviceIntent.setAction(TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT);
+ serviceIntent.putExtra(TransportConstants.CONNECTION_TYPE_EXTRA, TransportConstants.AOA_USB);
ComponentName startedService;
try {
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/SdlDeviceListener.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/SdlDeviceListener.java index 41ada091d..8e3682f50 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/SdlDeviceListener.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/SdlDeviceListener.java @@ -111,7 +111,7 @@ public class SdlDeviceListener { // set timeout = if first time seeing BT device, 30s, if not 15s int timeout = connectedDevice != null && isFirstStatusCheck(connectedDevice.getAddress()) ? 30000 : 15000; //Set our preference as false for this device for now - if(connectedDevice != null) { + if (connectedDevice != null) { setSDLConnectedStatus(contextWeakReference.get(), connectedDevice.getAddress(), false); } bluetoothHandler = new TransportHandler(this); @@ -190,7 +190,7 @@ public class SdlDeviceListener { */ public void sendStartService() { SdlDeviceListener sdlListener = this.provider.get(); - SdlPacket serviceProbe = SdlPacketFactory.createStartSession(SessionType.RPC, 0x00, (byte)0x01, (byte)0x00, false); + SdlPacket serviceProbe = SdlPacketFactory.createStartSession(SessionType.RPC, 0x00, (byte) 0x01, (byte) 0x00, false); serviceProbe.putTag(ControlFrameTags.RPC.StartService.PROTOCOL_VERSION, MAX_PROTOCOL_VERSION.toString()); byte[] constructed = serviceProbe.constructPacket(); if (sdlListener.bluetoothTransport != null && sdlListener.bluetoothTransport.getState() == MultiplexBluetoothTransport.STATE_CONNECTED) { @@ -216,7 +216,7 @@ public class SdlDeviceListener { } else { hashID = BitConverter.intFromByteArray(packet.getPayload(), 0); } - byte[] stopService = SdlPacketFactory.createEndSession(SessionType.RPC, (byte)packet.getSessionId(), 0, (byte)packet.getVersion(), hashID).constructPacket(); + byte[] stopService = SdlPacketFactory.createEndSession(SessionType.RPC, (byte) packet.getSessionId(), 0, (byte) packet.getVersion(), hashID).constructPacket(); if (sdlListener.bluetoothTransport != null && sdlListener.bluetoothTransport.getState() == MultiplexBluetoothTransport.STATE_CONNECTED) { sdlListener.bluetoothTransport.write(stopService, 0, stopService.length); } @@ -259,8 +259,11 @@ public class SdlDeviceListener { AndroidTools.saveVehicleType(sdlListener.contextWeakReference.get(), vehicleType, sdlListener.connectedDevice.getAddress()); boolean keepConnectionOpen = sdlListener.callback.onTransportConnected(sdlListener.contextWeakReference.get(), sdlListener.connectedDevice); if (!keepConnectionOpen) { - sdlListener.bluetoothTransport.stop(); - sdlListener.bluetoothTransport = null; + if (sdlListener.bluetoothTransport != null) { + sdlListener.bluetoothTransport.stop(); + sdlListener.bluetoothTransport = null; + sdlListener.bluetoothHandler = null; + } sdlListener.timeoutHandler.removeCallbacks(sdlListener.timeoutRunner); } } @@ -349,7 +352,7 @@ public class SdlDeviceListener { /** * Callback for the SdlDeviceListener. It will return if the supplied device makes a bluetooth - * connection on the SDL UUID RFCOMM chanel or not. Most of the time the only callback that + * connection on the SDL UUID RFCOMM channel or not. Most of the time the only callback that * matters will be the onTransportConnected. */ public interface Callback { diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/util/AndroidTools.java b/android/sdl_android/src/main/java/com/smartdevicelink/util/AndroidTools.java index 1f4ba2de0..2700d35d9 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/util/AndroidTools.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/util/AndroidTools.java @@ -48,6 +48,7 @@ import android.content.res.XmlResourceParser; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.BatteryManager; +import android.os.Build; import android.os.Bundle; import androidx.annotation.Nullable; @@ -69,6 +70,9 @@ import java.util.HashMap; import java.util.Hashtable; import java.util.List; +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_SCAN; + public class AndroidTools { @@ -199,6 +203,25 @@ public class AndroidTools { return sdlAppInfoList; } + public static boolean isPermissionGranted(String permissionName, Context context, String servicePackageName) { + PackageManager packageManager = context.getPackageManager(); + if (packageManager == null) { + return false; + } + PackageInfo packageInfo; + try { + packageInfo = packageManager.getPackageInfo(servicePackageName, PackageManager.GET_PERMISSIONS); + if (packageInfo == null) { + return false; + } + int permissionResult = packageManager.checkPermission(permissionName, packageInfo.packageName); + return permissionResult == PackageManager.PERMISSION_GRANTED; + } catch (NameNotFoundException e) { + e.printStackTrace(); + DebugTool.logError(TAG, "servicePackageName not found while checking " + permissionName + " permission", e); + return false; + } + } /** * Sends the provided intent to the specified destinations making it an explicit intent, rather @@ -331,7 +354,7 @@ public class AndroidTools { * * @param context a context to access Android system services through. * @param component a component name of a LocalRouterService. - * @param manifestFieldId a string resources id that indicates an unique name for the vehicle data in the manifest. + * @param manifestFieldId a string resources id that indicates a unique name for the vehicle data in the manifest. * @return The list of vehicle types, or null if an error occurred or field was not found. */ public static @Nullable List<VehicleType> getVehicleTypesFromManifest(Context context, ComponentName component, int manifestFieldId) { @@ -362,10 +385,10 @@ public class AndroidTools { XmlResourceParser parser = resources.getXml(xmlFieldId); return SdlAppInfo.deserializeSupportedVehicles(parser); } catch (PackageManager.NameNotFoundException e) { - DebugTool.logError(TAG, "Failed to get OEM vehicle data filter: " + e.getMessage()+ " - assume vehicle data is supported"); + DebugTool.logError(TAG, "Failed to get OEM vehicle data filter: " + e.getMessage() + " - assume vehicle data is supported"); return null; } catch (Resources.NotFoundException ex) { - DebugTool.logError(TAG, "Failed to find resource: " + ex.getMessage()+ " - assume vehicle data is supported"); + DebugTool.logError(TAG, "Failed to find resource: " + ex.getMessage() + " - assume vehicle data is supported"); return null; } } diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/util/SdlAppInfo.java b/android/sdl_android/src/main/java/com/smartdevicelink/util/SdlAppInfo.java index 7d5ade055..bfdec946e 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/util/SdlAppInfo.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/util/SdlAppInfo.java @@ -136,7 +136,7 @@ public class SdlAppInfo { if (!contextPackageName.equals(packageName)) { try { Context appContext = context.createPackageContext(packageName, 0); - if (appContext == null){ + if (appContext == null) { DebugTool.logError(TAG, "Failed to create context with the given package name"); return; } @@ -240,7 +240,7 @@ public class SdlAppInfo { if (model == null && modelYear == null && trim == null) { vehicleMakesList.add(vehicleMake); - } else if (model != null){ + } else if (model != null) { vehicleMake.setModel(model); if (modelYear != null) { vehicleMake.setModelYear(modelYear); @@ -271,13 +271,13 @@ public class SdlAppInfo { * @return true if vehicle type is supported. */ public static boolean checkIfVehicleSupported(List<VehicleType> supportedVehicleList, VehicleType connectedVehicle) { - if (supportedVehicleList == null || supportedVehicleList.isEmpty() || connectedVehicle == null || connectedVehicle.getStore() == null|| connectedVehicle.getStore().isEmpty()) { + if (supportedVehicleList == null || supportedVehicleList.isEmpty() || connectedVehicle == null || connectedVehicle.getStore() == null || connectedVehicle.getStore().isEmpty()) { return true; } if (supportedVehicleList.contains(connectedVehicle)) { return true; } - for (VehicleType supportedVehicle: supportedVehicleList) { + for (VehicleType supportedVehicle : supportedVehicleList) { boolean areVehicleMakesEqual = CompareUtils.areStringsEqual(supportedVehicle.getMake(), connectedVehicle.getMake(), true, false); if (areVehicleMakesEqual) { String supportedVehicleModel = supportedVehicle.getModel(); diff --git a/android/sdl_android/src/main/res/values/sdl.xml b/android/sdl_android/src/main/res/values/sdl.xml index 7bb919f0b..bd96c54ab 100644 --- a/android/sdl_android/src/main/res/values/sdl.xml +++ b/android/sdl_android/src/main/res/values/sdl.xml @@ -2,7 +2,7 @@ <resources> <string name="sdl_router_service_version_name" translatable="false">sdl_router_version</string> - <integer name="sdl_router_service_version_value">15</integer> + <integer name="sdl_router_service_version_value">16</integer> <string name="sdl_router_service_is_custom_name" translatable="false">sdl_custom_router</string> <string name="sdl_oem_vehicle_type_filter_name" translatable="false">sdl_oem_vehicle_type</string> diff --git a/android/sdl_android/src/main/res/values/strings.xml b/android/sdl_android/src/main/res/values/strings.xml index 89322299f..36a089366 100644 --- a/android/sdl_android/src/main/res/values/strings.xml +++ b/android/sdl_android/src/main/res/values/strings.xml @@ -5,6 +5,7 @@ <string name="lockscreen_device_image_description">Device Icon</string> <string name="default_lockscreen_warning_message">Swipe down to dismiss, acknowledging that you are not the driver.</string> <string name="spp_out_of_resource">Too many apps are using Bluetooth</string> + <string name="allow_bluetooth_permissions">Please grant this app the Nearby Devices Permission to use bluetooth</string> <string name="notification_title">SmartDeviceLink</string> <string name="sdl_error_notification_channel_name">SDL Error</string> </resources>
\ No newline at end of file diff --git a/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java b/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java index db775e10e..d71cf35f2 100644 --- a/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java @@ -188,52 +188,57 @@ abstract class BaseSdlManager { if ((actualLanguage != null && !actualLanguage.equals(language)) || (actualHMILanguage != null && !actualHMILanguage.equals(hmiLanguage))) { - final LifecycleConfigurationUpdate lcu = managerListener.managerShouldUpdateLifecycle(actualLanguage, actualHMILanguage); - - ChangeRegistration changeRegistration; - changeRegistration = new ChangeRegistration(actualLanguage, actualHMILanguage); - - if (lcu != null) { - changeRegistration.setAppName(lcu.getAppName()); - changeRegistration.setNgnMediaScreenAppName(lcu.getShortAppName()); - changeRegistration.setTtsName(lcu.getTtsName()); - changeRegistration.setVrSynonyms(lcu.getVoiceRecognitionCommandNames()); - changeRegistration.setOnRPCResponseListener(new OnRPCResponseListener() { - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (response.getSuccess()) { - try { - DebugTool.logInfo(TAG, response.serializeJSON().toString()); - } catch (JSONException e) { - DebugTool.logError(TAG, "Error attempting to serialize ChangeRegistrationResponse", e); - } - - // go through and change sdlManager properties that were changed via the LCU update - hmiLanguage = actualHMILanguage; - language = actualLanguage; - - if (lcu.getAppName() != null) { - appName = lcu.getAppName(); - } - - if (lcu.getShortAppName() != null) { - shortAppName = lcu.getShortAppName(); + if(managerListener != null) { + final LifecycleConfigurationUpdate lcu = managerListener.managerShouldUpdateLifecycle(actualLanguage, actualHMILanguage); + + ChangeRegistration changeRegistration; + changeRegistration = new ChangeRegistration(actualLanguage, actualHMILanguage); + + if (lcu != null) { + changeRegistration.setAppName(lcu.getAppName()); + changeRegistration.setNgnMediaScreenAppName(lcu.getShortAppName()); + changeRegistration.setTtsName(lcu.getTtsName()); + changeRegistration.setVrSynonyms(lcu.getVoiceRecognitionCommandNames()); + changeRegistration.setOnRPCResponseListener(new OnRPCResponseListener() { + @Override + public void onResponse(int correlationId, RPCResponse response) { + if (response.getSuccess()) { + try { + DebugTool.logInfo(TAG, response.serializeJSON().toString()); + } catch (JSONException e) { + DebugTool.logError(TAG, "Error attempting to serialize ChangeRegistrationResponse", e); + } + + // go through and change sdlManager properties that were changed via the LCU update + hmiLanguage = actualHMILanguage; + language = actualLanguage; + + if (lcu.getAppName() != null) { + appName = lcu.getAppName(); + } + + if (lcu.getShortAppName() != null) { + shortAppName = lcu.getShortAppName(); + } + + if (lcu.getTtsName() != null) { + ttsChunks = lcu.getTtsName(); + } + + if (lcu.getVoiceRecognitionCommandNames() != null) { + vrSynonyms = lcu.getVoiceRecognitionCommandNames(); + } + } else { + DebugTool.logError(TAG, "Change Registration onError: " + response.getResultCode() + " | Info: " + response.getInfo()); + retryChangeRegistration(); } - - if (lcu.getTtsName() != null) { - ttsChunks = lcu.getTtsName(); - } - - if (lcu.getVoiceRecognitionCommandNames() != null) { - vrSynonyms = lcu.getVoiceRecognitionCommandNames(); - } - } else { - DebugTool.logError(TAG, "Change Registration onError: " + response.getResultCode() + " | Info: " + response.getInfo()); - retryChangeRegistration(); } - } - }); - this.sendRPC(changeRegistration); + }); + this.sendRPC(changeRegistration); + } + } + else { + DebugTool.logError(TAG, "SdlManagerListener is null"); } } } diff --git a/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java b/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java index aab50a280..df2a4f3f7 100644 --- a/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java @@ -244,7 +244,7 @@ class UploadFileOperation extends Task { try { this.inputStream.close(); } catch (IOException e) { - DebugTool.logError(TAG,"Error attempting to close input stream", e); + DebugTool.logError(TAG, "Error attempting to close input stream", e); } } @@ -353,7 +353,7 @@ class UploadFileOperation extends Task { try { bytesRead = inputStream.read(buffer, 0, size); } catch (IOException e) { - DebugTool.logError(TAG,"Error attempting to read from input stream", e); + DebugTool.logError(TAG, "Error attempting to read from input stream", e); } if (bytesRead > 0) { @@ -388,7 +388,7 @@ class UploadFileOperation extends Task { try { size = inputStream.available(); } catch (IOException e) { - DebugTool.logError(TAG,"Error trying to get input stream size", e); + DebugTool.logError(TAG, "Error trying to get input stream size", e); } } return size; diff --git a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java index 6602e7085..f1b20237b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java @@ -32,6 +32,12 @@ package com.smartdevicelink.managers.lifecycle; +import static com.smartdevicelink.managers.BaseSubManager.SETTING_UP; +import static com.smartdevicelink.managers.BaseSubManager.READY; +import static com.smartdevicelink.managers.BaseSubManager.LIMITED; +import static com.smartdevicelink.managers.BaseSubManager.SHUTDOWN; +import static com.smartdevicelink.managers.BaseSubManager.ERROR; + import androidx.annotation.NonNull; import androidx.annotation.RestrictTo; @@ -116,7 +122,10 @@ abstract class BaseLifecycleManager { ON_UPDATE_LISTENER_LOCK = new Object(), ON_REQUEST_LISTENER_LOCK = new Object(), ON_NOTIFICATION_LISTENER_LOCK = new Object(); + protected static final Object SESSION_LOCK = new Object(); + private final Object STATE_LOCK = new Object(); + private int state; SdlSession session; final AppConfig appConfig; Version rpcSpecVersion = MAX_SUPPORTED_RPC_VERSION; @@ -141,6 +150,7 @@ abstract class BaseLifecycleManager { DisplayCapabilities initialMediaCapabilities; BaseLifecycleManager(AppConfig appConfig, BaseTransportConfig config, LifecycleListener listener) { + transitionToState(SETTING_UP); this.appConfig = appConfig; this._transportConfig = config; this.lifecycleListener = listener; @@ -151,9 +161,13 @@ abstract class BaseLifecycleManager { public void start() { try { - session.startSession(); + synchronized (SESSION_LOCK) { + if (session != null) { + session.startSession(); + } + } } catch (SdlException e) { - DebugTool.logError(TAG,"Error attempting to start session", e); + DebugTool.logError(TAG, "Error attempting to start session", e); } } @@ -161,19 +175,28 @@ abstract class BaseLifecycleManager { * Start a secured RPC service */ public void startRPCEncryption() { - if (session != null) { - session.startService(SessionType.RPC, true); + synchronized (SESSION_LOCK) { + if (session != null) { + session.startService(SessionType.RPC, true); + } } } public synchronized void stop() { - if (session != null) { - session.close(); - session = null; + DebugTool.logInfo(TAG, "LifecycleManager stop requested"); + clean(true); + transitionToState(SHUTDOWN); + } + + protected void transitionToState(int state) { + synchronized (STATE_LOCK) { + this.state = state; } - if (taskmaster != null) { - taskmaster.shutdown(); - taskmaster = null; + } + + public int getState() { + synchronized (STATE_LOCK) { + return state; } } @@ -194,8 +217,10 @@ abstract class BaseLifecycleManager { } Version getProtocolVersion() { - if (session != null && session.getProtocolVersion() != null) { - return session.getProtocolVersion(); + synchronized (SESSION_LOCK) { + if (session != null && session.getProtocolVersion() != null) { + return session.getProtocolVersion(); + } } return new Version(1, 0, 0); } @@ -301,10 +326,12 @@ abstract class BaseLifecycleManager { } private boolean isConnected() { - if (session != null) { - return session.getIsConnected(); - } else { - return false; + synchronized (SESSION_LOCK) { + if (session != null) { + return session.getIsConnected(); + } else { + return false; + } } } @@ -331,6 +358,7 @@ abstract class BaseLifecycleManager { void onClose(String info, Exception e, SdlDisconnectedReason reason) { DebugTool.logInfo(TAG, "onClose"); + transitionToState(SHUTDOWN); if (lifecycleListener != null) { lifecycleListener.onClosed((LifecycleManager) this, info, e, reason); } @@ -387,10 +415,7 @@ abstract class BaseLifecycleManager { } if (minimumRPCVersion != null && minimumRPCVersion.isNewerThan(rpcSpecVersion) == 1) { DebugTool.logWarning(TAG, String.format("Disconnecting from head unit, the configured minimum RPC version %s is greater than the supported RPC version %s", minimumRPCVersion, rpcSpecVersion)); - UnregisterAppInterface msg = new UnregisterAppInterface(); - msg.setCorrelationID(UNREGISTER_APP_INTERFACE_CORRELATION_ID); - sendRPCMessagePrivate(msg, true); - clean(); + clean(true); onClose("RPC spec version not supported: " + rpcSpecVersion.toString(), null, SdlDisconnectedReason.MINIMUM_RPC_VERSION_HIGHER_THAN_SUPPORTED); return; } @@ -399,15 +424,19 @@ abstract class BaseLifecycleManager { VehicleType vehicleType = raiResponse.getVehicleType(); String systemSoftwareVersion = raiResponse.getSystemSoftwareVersion(); if (vehicleType != null || systemSoftwareVersion != null) { - saveVehicleType(session.getActiveTransports(), vehicleType); + + List<TransportRecord> activeTransports = null; + synchronized (SESSION_LOCK) { + if (session != null) { + activeTransports = session.getActiveTransports(); + } + } + saveVehicleType(activeTransports, vehicleType); SystemInfo systemInfo = new SystemInfo(vehicleType, systemSoftwareVersion, null); boolean validSystemInfo = lifecycleListener.onSystemInfoReceived(systemInfo); if (!validSystemInfo) { DebugTool.logWarning(TAG, "Disconnecting from head unit, the system info was not accepted."); - UnregisterAppInterface msg = new UnregisterAppInterface(); - msg.setCorrelationID(UNREGISTER_APP_INTERFACE_CORRELATION_ID); - sendRPCMessagePrivate(msg, true); - clean(); + clean(true); onClose("System not supported", null, SdlDisconnectedReason.DEFAULT); return; } @@ -425,6 +454,7 @@ abstract class BaseLifecycleManager { DebugTool.logInfo(TAG, "on hmi status"); boolean shouldInit = currentHMIStatus == null; currentHMIStatus = (OnHMIStatus) message; + transitionToState(READY); if (lifecycleListener != null && shouldInit) { lifecycleListener.onConnected((LifecycleManager) BaseLifecycleManager.this); } @@ -494,7 +524,7 @@ abstract class BaseLifecycleManager { if (!onAppInterfaceUnregistered.getReason().equals(AppInterfaceUnregisteredReason.LANGUAGE_CHANGE)) { DebugTool.logInfo(TAG, "on app interface unregistered"); - clean(); + clean(false); onClose("OnAppInterfaceUnregistered received from head unit", null, SdlDisconnectedReason.APP_INTERFACE_UNREG); } else { DebugTool.logInfo(TAG, "re-registering for language change"); @@ -502,9 +532,11 @@ abstract class BaseLifecycleManager { } break; case UNREGISTER_APP_INTERFACE: - DebugTool.logInfo(TAG, "unregister app interface"); - clean(); - onClose("UnregisterAppInterface response received from head unit", null, SdlDisconnectedReason.APP_INTERFACE_UNREG); + DebugTool.logInfo(TAG, "Unregister app interface response received"); + //Since only the library sends the UnregisterAppInterface requests, we know + //that the correct logic flows already happen based on where the call to send + //the request happens. There is also a SYNC4 bug that holds onto the response + //until the app reconnects within the same transport session. break; } } @@ -799,8 +831,10 @@ abstract class BaseLifecycleManager { final ProtocolMessage pm = new ProtocolMessage(); pm.setData(msgBytes); - if (session != null) { - pm.setSessionID((byte) session.getSessionId()); + synchronized (SESSION_LOCK) { + if (session != null) { + pm.setSessionID((byte) session.getSessionId()); + } } pm.setMessageType(MessageType.RPC); @@ -843,7 +877,7 @@ abstract class BaseLifecycleManager { } // HAX: Issue #1690, Ford Sync bug returning incorrect display capabilities (https://github.com/smartdevicelink/sdl_java_suite/issues/1690). Save the next desired layout type to the update capabilities when the SetDisplayLayout response is received if (FunctionID.SET_DISPLAY_LAYOUT.toString().equals(message.getFunctionName())) { - lastDisplayLayoutRequestTemplate = ((SetDisplayLayout)message).getDisplayLayout(); + lastDisplayLayoutRequestTemplate = ((SetDisplayLayout) message).getDisplayLayout(); } } else if (RPCMessage.KEY_RESPONSE.equals(message.getMessageType())) { // Response Specifics RPCResponse response = (RPCResponse) message; @@ -868,10 +902,14 @@ abstract class BaseLifecycleManager { pm.setPriorityCoefficient(1); } - session.sendMessage(pm); + synchronized (SESSION_LOCK) { + if (session != null) { + session.sendMessage(pm); + } + } } catch (OutOfMemoryError e) { - DebugTool.logError(TAG,"Error attempting to send RPC message.", e); + DebugTool.logError(TAG, "Error attempting to send RPC message.", e); } } @@ -940,22 +978,27 @@ abstract class BaseLifecycleManager { DebugTool.logInfo(TAG, "on protocol session started"); if (minimumProtocolVersion != null && minimumProtocolVersion.isNewerThan(version) == 1) { DebugTool.logWarning(TAG, String.format("Disconnecting from head unit, the configured minimum protocol version %s is greater than the supported protocol version %s", minimumProtocolVersion, getProtocolVersion())); - session.endService(SessionType.RPC); - clean(); + clean(false); onClose("Protocol version not supported: " + version, null, SdlDisconnectedReason.MINIMUM_PROTOCOL_VERSION_HIGHER_THAN_SUPPORTED); return; } if (systemInfo != null && lifecycleListener != null) { didCheckSystemInfo = true; - saveVehicleType(session.getActiveTransports(), systemInfo.getVehicleType()); + List<TransportRecord> activeTransports = null; + synchronized (SESSION_LOCK) { + if (session != null) { + activeTransports = session.getActiveTransports(); + } + } + saveVehicleType(activeTransports, systemInfo.getVehicleType()); boolean validSystemInfo = lifecycleListener.onSystemInfoReceived(systemInfo); if (!validSystemInfo) { DebugTool.logWarning(TAG, "Disconnecting from head unit, the system info was not accepted."); - session.endService(SessionType.RPC); - clean(); + clean(false); onClose("System not supported", null, SdlDisconnectedReason.DEFAULT); return; + } //If the vehicle is acceptable, init security lib setSecurityLibraryIfAvailable(systemInfo.getVehicleType()); @@ -1024,8 +1067,10 @@ abstract class BaseLifecycleManager { @Override public boolean isConnected() { synchronized (BaseLifecycleManager.this) { - if (BaseLifecycleManager.this.session != null) { - return BaseLifecycleManager.this.session.getIsConnected(); + synchronized (SESSION_LOCK) { + if (BaseLifecycleManager.this.session != null) { + return BaseLifecycleManager.this.session.getIsConnected(); + } } } return false; @@ -1034,8 +1079,10 @@ abstract class BaseLifecycleManager { @Override public void addServiceListener(SessionType serviceType, ISdlServiceListener sdlServiceListener) { synchronized (BaseLifecycleManager.this) { - if(BaseLifecycleManager.this.session != null ){ - BaseLifecycleManager.this.session.addServiceListener(serviceType, sdlServiceListener); + synchronized (SESSION_LOCK) { + if (BaseLifecycleManager.this.session != null) { + BaseLifecycleManager.this.session.addServiceListener(serviceType, sdlServiceListener); + } } } } @@ -1043,8 +1090,10 @@ abstract class BaseLifecycleManager { @Override public void removeServiceListener(SessionType serviceType, ISdlServiceListener sdlServiceListener) { synchronized (BaseLifecycleManager.this) { - if (BaseLifecycleManager.this.session != null) { - BaseLifecycleManager.this.session.removeServiceListener(serviceType, sdlServiceListener); + synchronized (SESSION_LOCK) { + if (BaseLifecycleManager.this.session != null) { + BaseLifecycleManager.this.session.removeServiceListener(serviceType, sdlServiceListener); + } } } } @@ -1114,8 +1163,10 @@ abstract class BaseLifecycleManager { @Override public boolean isTransportForServiceAvailable(SessionType serviceType) { synchronized (BaseLifecycleManager.this) { - if (BaseLifecycleManager.this.session != null) { - return BaseLifecycleManager.this.session.isTransportForServiceAvailable(serviceType); + synchronized (SESSION_LOCK) { + if (BaseLifecycleManager.this.session != null) { + return BaseLifecycleManager.this.session.isTransportForServiceAvailable(serviceType); + } } } return false; @@ -1125,11 +1176,11 @@ abstract class BaseLifecycleManager { @Override public SdlMsgVersion getSdlMsgVersion() { SdlMsgVersion msgVersion; - if(rpcSpecVersion != null) { + if (rpcSpecVersion != null) { msgVersion = new SdlMsgVersion(rpcSpecVersion.getMajor(), rpcSpecVersion.getMinor()); msgVersion.setPatchVersion(rpcSpecVersion.getPatch()); } else { - msgVersion = new SdlMsgVersion(1,0); + msgVersion = new SdlMsgVersion(1, 0); } return msgVersion; @@ -1144,8 +1195,10 @@ abstract class BaseLifecycleManager { @Override public long getMtu(SessionType serviceType) { synchronized (BaseLifecycleManager.this) { - if (BaseLifecycleManager.this.session != null) { - return BaseLifecycleManager.this.session.getMtu(serviceType); + synchronized (SESSION_LOCK) { + if (BaseLifecycleManager.this.session != null) { + return BaseLifecycleManager.this.session.getMtu(serviceType); + } } } return SdlProtocolBase.V1_V2_MTU_SIZE; @@ -1229,7 +1282,20 @@ abstract class BaseLifecycleManager { return null; } - void clean() { + void clean(boolean sendUnregisterAppInterface) { + int state = getState(); + if (state == SHUTDOWN || state == ERROR) { + DebugTool.logInfo(TAG, "No need to clean, LCM is already cleaned: " + state); + return; + } + + if (sendUnregisterAppInterface) { + DebugTool.logInfo(TAG, "Requesting to unregister from device"); + UnregisterAppInterface uai = new UnregisterAppInterface(); + uai.setCorrelationID(UNREGISTER_APP_INTERFACE_CORRELATION_ID); + sendRPCMessagePrivate(uai, true); + } + firstTimeFull = true; currentHMIStatus = null; lastDisplayLayoutRequestTemplate = null; @@ -1246,8 +1312,11 @@ abstract class BaseLifecycleManager { if (rpcRequestListeners != null) { rpcRequestListeners.clear(); } - if (session != null && session.getIsConnected()) { - session.close(); + synchronized (SESSION_LOCK) { + if (session != null && session.getIsConnected()) { + session.close(); + session = null; + } } if (encryptionLifecycleManager != null) { encryptionLifecycleManager.dispose(); @@ -1302,9 +1371,11 @@ abstract class BaseLifecycleManager { if ((sec != null) && (sec.getMakeList() != null)) { if (sec.getMakeList().contains(make)) { sec.setAppId(appConfig.getAppID()); - if (session != null) { - session.setSdlSecurity(sec); - sec.handleSdlSession(session); + synchronized (SESSION_LOCK) { + if (session != null) { + session.setSdlSecurity(sec); + sec.handleSdlSession(session); + } } return; } @@ -1323,12 +1394,13 @@ abstract class BaseLifecycleManager { this.rpcRequestListeners = new HashMap<>(); this.systemCapabilityManager = new SystemCapabilityManager(internalInterface); setupInternalRpcListeners(); + } abstract void cycle(SdlDisconnectedReason disconnectedReason); - void saveVehicleType(String address, VehicleType type){ + void saveVehicleType(String address, VehicleType type) { } void saveVehicleType(List<TransportRecord> activeTransports, VehicleType type) { diff --git a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java index 33a778e58..1f6c663a8 100644 --- a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java @@ -590,12 +590,12 @@ abstract class BaseSystemCapabilityManager { final GetSystemCapability request = new GetSystemCapability(); request.setSystemCapabilityType(systemCapabilityType); - /* - The subscription flag in the request should be set based on multiple variables: - - if subscribe is null (no change), willSubscribe = current subscription status, or false if the HU does not support subscriptions - - if subscribe is false, then willSubscribe = false - - if subscribe is true and the HU supports subscriptions, then willSubscribe = true - */ + /* + The subscription flag in the request should be set based on multiple variables: + - if subscribe is null (no change), willSubscribe = current subscription status, or false if the HU does not support subscriptions + - if subscribe is false, then willSubscribe = false + - if subscribe is true and the HU supports subscriptions, then willSubscribe = true + */ boolean shouldSubscribe = (subscribe != null) ? subscribe : isSubscribedToSystemCapability(systemCapabilityType); final boolean willSubscribe = shouldSubscribe && supportsSubscriptions(); request.setSubscribe(willSubscribe); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/DispatchGroup.java b/base/src/main/java/com/smartdevicelink/managers/screen/DispatchGroup.java index f6dccb715..352f0bbc4 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/DispatchGroup.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/DispatchGroup.java @@ -38,20 +38,25 @@ package com.smartdevicelink.managers.screen; class DispatchGroup { private int count; private Runnable runnable; + DispatchGroup() { count = 0; } + synchronized void enter() { count++; } + synchronized void leave() { count--; run(); } + void notify(Runnable runnable) { this.runnable = runnable; run(); } + private void run() { if (count <= 0 && runnable != null) { runnable.run(); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/PresentAlertOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/PresentAlertOperation.java index 9957ad309..a85901d3c 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/PresentAlertOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/PresentAlertOperation.java @@ -194,7 +194,7 @@ public class PresentAlertOperation extends Task { List<SdlFile> filesToBeUploaded = new ArrayList<>(); for (TTSChunk ttsChunk : alertView.getAudio().getAudioData()) { - if(ttsChunk.getType() != SpeechCapabilities.FILE){ + if (ttsChunk.getType() != SpeechCapabilities.FILE) { continue; } SdlFile audioFile = alertView.getAudio().getAudioFiles().get(ttsChunk.getText()); @@ -300,7 +300,7 @@ public class PresentAlertOperation extends Task { public void onResponse(int correlationId, RPCResponse response) { if (!response.getSuccess()) { DebugTool.logError(TAG, "There was an error presenting the alert: " + response.getInfo()); - } else { + } else { DebugTool.logInfo(TAG, "Alert finished presenting"); } finishOperation(response.getSuccess(), ((AlertResponse) response).getTryAgainTime()); @@ -401,12 +401,12 @@ public class PresentAlertOperation extends Task { private List<TTSChunk> getTTSChunksForAlert(AlertView alertView) { AlertAudioData alertAudioData = alertView.getAudio(); List<TTSChunk> ttsChunks = new ArrayList<>(); - for (TTSChunk chunk : alertAudioData.getAudioData()) { - if (chunk.getType() == SpeechCapabilities.FILE && !supportsAlertAudioFile()) { - continue; - } - ttsChunks.add(chunk); + for (TTSChunk chunk : alertAudioData.getAudioData()) { + if (chunk.getType() == SpeechCapabilities.FILE && !supportsAlertAudioFile()) { + continue; } + ttsChunks.add(chunk); + } return ttsChunks.size() > 0 ? ttsChunks : null; } @@ -416,6 +416,14 @@ public class PresentAlertOperation extends Task { * @return True if soft button images are currently supported; false if not. */ private boolean supportsSoftButtonImages() { + if (currentWindowCapability == null || + currentWindowCapability.getSoftButtonCapabilities() == null || + currentWindowCapability.getSoftButtonCapabilities().size() == 0 || + currentWindowCapability.getSoftButtonCapabilities().get(0) == null + ) { + return true; + } + SoftButtonCapabilities softButtonCapabilities = currentWindowCapability.getSoftButtonCapabilities().get(0); return softButtonCapabilities.getImageSupported().booleanValue(); } @@ -446,7 +454,7 @@ public class PresentAlertOperation extends Task { if (nonNullFields.isEmpty()) { return alert; } - int numberOfLines = currentWindowCapability!= null ? ManagerUtility.WindowCapabilityUtility.getMaxNumberOfAlertFieldLines(currentWindowCapability) : 3; + int numberOfLines = currentWindowCapability != null ? ManagerUtility.WindowCapabilityUtility.getMaxNumberOfAlertFieldLines(currentWindowCapability) : 3; switch (numberOfLines) { case 1: alert = assembleOneLineAlertText(alert, nonNullFields); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonObject.java b/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonObject.java index 76301bc07..2a9994ead 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonObject.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonObject.java @@ -38,7 +38,6 @@ import com.smartdevicelink.proxy.rpc.OnButtonEvent; import com.smartdevicelink.proxy.rpc.OnButtonPress; import com.smartdevicelink.proxy.rpc.SoftButton; import com.smartdevicelink.util.DebugTool; - import java.util.Collections; import java.util.List; @@ -50,7 +49,7 @@ import java.util.List; * * @see SoftButtonState */ -public class SoftButtonObject implements Cloneable{ +public class SoftButtonObject implements Cloneable { private static final String TAG = "SoftButtonObject"; static final int SOFT_BUTTON_ID_NOT_SET_VALUE = -1; static final int SOFT_BUTTON_ID_MIN_VALUE = 0; @@ -73,12 +72,24 @@ public class SoftButtonObject implements Cloneable{ */ public SoftButtonObject(@NonNull String name, @NonNull List<SoftButtonState> states, @NonNull String initialStateName, OnEventListener onEventListener) { - // Make sure there aren't two states with the same name - if (hasTwoStatesOfSameName(states)) { - DebugTool.logError(TAG, "Two states have the same name in states list for soft button object"); - return; + boolean repeatedStateNames = hasTwoStatesOfSameName(states); + + boolean hasStateWithInitialName = false; + for (SoftButtonState state : states) { + if(state.getName().equals(initialStateName)) { + hasStateWithInitialName = true; + break; + } } + if (repeatedStateNames) { + DebugTool.logError(TAG, "A SoftButtonObject must have states with different names."); + return; + } + if (!hasStateWithInitialName) { + DebugTool.logError(TAG, "A SoftButtonObject must have a state with initialStateName."); + return; + } this.name = name; this.states = states; this.currentStateName = initialStateName; @@ -259,11 +270,35 @@ public class SoftButtonObject implements Cloneable{ } /** - * Set the the SoftButtonState list + * Set the SoftButtonState list * * @param states a list of the object's soft button states. <strong>states should be unique for every SoftButtonObject. A SoftButtonState instance cannot be reused for multiple SoftButtonObjects.</strong> */ public void setStates(@NonNull List<SoftButtonState> states) { + + boolean repeatedStateNames = hasTwoStatesOfSameName(states); + + if (repeatedStateNames) { + DebugTool.logError(TAG, "A SoftButtonObject must have states with different names."); + return; + } + + if (states.isEmpty()) { + DebugTool.logError(TAG, "A SoftButtonObject must contain at least one state"); + return; + } + + boolean hasStateWithCurrentName = false; + for (SoftButtonState state : states) { + if(state.getName().equals(currentStateName)) { + hasStateWithCurrentName = true; + break; + } + } + if (!hasStateWithCurrentName) { + DebugTool.logError(TAG, "A SoftButtonObject setting states must contain a state with the name " + currentStateName + "."); + } + this.states = states; } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonReplaceOperation.java index c6578ff2e..b157c4cb9 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonReplaceOperation.java @@ -116,7 +116,7 @@ class SoftButtonReplaceOperation extends Task { List<SdlArtwork> initialStatesToBeUploaded = new ArrayList<>(); for (SoftButtonObject softButtonObject : softButtonObjects) { SoftButtonState softButtonState = softButtonObject.getCurrentState(); - if (softButtonState != null && fileManager.get()!= null && fileManager.get().fileNeedsUpload(softButtonState.getArtwork()) && supportsSoftButtonImages()) { + if (softButtonState != null && fileManager.get() != null && fileManager.get().fileNeedsUpload(softButtonState.getArtwork()) && supportsSoftButtonImages()) { initialStatesToBeUploaded.add(softButtonState.getArtwork()); } } 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 46d478000..9a4f1bedb 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 @@ -40,7 +40,7 @@ import com.smartdevicelink.util.DebugTool; import java.util.ArrayList; import java.util.List; -public class ChoiceCell implements Cloneable{ +public class ChoiceCell implements Cloneable { private String text, secondaryText, tertiaryText; private List<String> voiceCommands; private SdlArtwork artwork, secondaryArtwork; 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 index 91a32ea0b..2eb04342e 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -110,6 +110,7 @@ class PreloadPresentChoicesOperation extends Task { Integer selectedCellRow; KeyboardListener keyboardListener; final SdlMsgVersion sdlMsgVersion; + private enum SDLPreloadPresentChoicesOperationState { NOT_STARTED, UPLOADING_IMAGES, @@ -120,10 +121,11 @@ class PreloadPresentChoicesOperation extends Task { 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) { + Boolean isVROptional, LinkedHashSet<ChoiceCell> cellsToPreload, HashSet<ChoiceCell> loadedCells, BaseChoiceSetManager.ChoicesOperationCompletionListener listener) { super("PreloadPresentChoiceOperation"); this.internalInterface = new WeakReference<>(internalInterface); this.fileManager = new WeakReference<>(fileManager); @@ -207,14 +209,14 @@ class PreloadPresentChoicesOperation extends Task { @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) { + if (getState() == CANCELED || !success) { finishOperation(false); return; } preloadCells(new CompletionListener() { @Override public void onComplete(boolean success) { - if (getState()==CANCELED || !success) { + if (getState() == CANCELED || !success) { finishOperation(false); return; } @@ -227,7 +229,7 @@ class PreloadPresentChoicesOperation extends Task { updateKeyboardProperties(new CompletionListener() { @Override public void onComplete(boolean success) { - if (getState()==CANCELED || !success) { + if (getState() == CANCELED || !success) { finishOperation(false); return; } @@ -383,7 +385,7 @@ class PreloadPresentChoicesOperation extends Task { void resetKeyboardProperties(final CompletionListener listener) { this.currentState = SDLPreloadPresentChoicesOperationState.RESETTING_KEYBOARD_PROPERTIES; if (this.keyboardListener == null || this.originalKeyboardProperties == null) { - if(listener != null) { + if (listener != null) { listener.onComplete(true); finishOperation(true); return; @@ -482,7 +484,7 @@ class PreloadPresentChoicesOperation extends Task { DebugTool.logInfo(TAG, "Canceling the operation before a present."); this.cancelTask(); return; - }else if (sdlMsgVersion.getMajorVersion() < 6) { + } else if (sdlMsgVersion.getMajorVersion() < 6) { DebugTool.logWarning(TAG, "Canceling a presented choice set is not supported on this head unit"); this.cancelTask(); return; @@ -591,7 +593,7 @@ class PreloadPresentChoicesOperation extends Task { int lastUsedId = usedIds.get(usedIds.size() - 1); if (lastUsedId < MAX_CHOICE_ID) { choiceId = lastUsedId + 1; - return choiceId; + return choiceId; } // All our easy options are gone. Find and grab an empty slot from within the sorted list @@ -646,7 +648,7 @@ class PreloadPresentChoicesOperation extends Task { } private void transferUniqueNamesFromCells(ArrayList<ChoiceCell> fromCells, ArrayList<ChoiceCell> toCells) { - for (int i = 0; i< fromCells.size(); i++) { + for (int i = 0; i < fromCells.size(); i++) { toCells.get(i).setUniqueTextId(fromCells.get(i).getUniqueTextId()); } } @@ -711,7 +713,7 @@ class PreloadPresentChoicesOperation extends Task { 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)) { + if (i != dictCounter.get(cellKey).get(i - 1)) { lowestMissingUniqueId = i; break; } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseVoiceCommandManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseVoiceCommandManager.java index 9c5c39c21..ec07e5305 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseVoiceCommandManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseVoiceCommandManager.java @@ -76,7 +76,7 @@ abstract class BaseVoiceCommandManager extends BaseSubManager { this.transactionQueue = newTransactionQueue(); - currentHMILevel = null; + currentHMILevel = null; addListeners(); lastVoiceCommandId = voiceCommandIdMin; } @@ -217,21 +217,27 @@ abstract class BaseVoiceCommandManager extends BaseSubManager { List<VoiceCommand> validatedVoiceCommands = new ArrayList<>(); for (VoiceCommand voiceCommand : voiceCommands) { if (voiceCommand == null) { + DebugTool.logWarning(TAG, "Voice command is null, it will not be uploaded"); continue; } List<String> voiceCommandStrings = new ArrayList<>(); for (String voiceCommandString : voiceCommand.getVoiceCommands()) { if (voiceCommandString == null) { + DebugTool.logWarning(TAG, "Removing null string from voice command"); continue; } String trimmedString = voiceCommandString.trim(); if (trimmedString.length() > 0) { voiceCommandStrings.add(trimmedString); + } else { + DebugTool.logWarning(TAG, "Empty string removed from voice command"); } } if (voiceCommandStrings.size() > 0) { voiceCommand.setVoiceCommands(voiceCommandStrings); validatedVoiceCommands.add(voiceCommand); + } else { + DebugTool.logWarning(TAG, "Voice command will not be uploaded as it contained no valid strings"); } } return validatedVoiceCommands; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java index b6fa0266c..aea0d10d6 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java @@ -112,7 +112,7 @@ class DynamicMenuUpdateAlgorithm { * Builds a 1-1 list of Deletes for every element in the array * @param oldMenu The old menu list */ - static List<MenuCellState> buildAllDeleteStatusesForMenu (List<MenuCell> oldMenu){ + static List<MenuCellState> buildAllDeleteStatusesForMenu(List<MenuCell> oldMenu) { List<MenuCellState> oldMenuStatus = new ArrayList<>(oldMenu.size()); for (int index = 0; index < oldMenu.size(); index++) { oldMenuStatus.add(MenuCellState.DELETE); @@ -124,7 +124,7 @@ class DynamicMenuUpdateAlgorithm { * Builds a 1-1 list of Adds for every element in the list * @param newMenu The new menu list */ - static List<MenuCellState> buildAllAddStatusesForMenu (List<MenuCell> newMenu){ + static List<MenuCellState> buildAllAddStatusesForMenu(List<MenuCell> newMenu) { List<MenuCellState> newMenuStatus = new ArrayList<>(newMenu.size()); for (int index = 0; index < newMenu.size(); index++) { newMenuStatus.add(MenuCellState.ADD); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java index eaf91ae76..5b8e98ef8 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java @@ -388,7 +388,7 @@ public class MenuCell implements Cloneable { * * @return the cell's tertiaryText */ - public String getTertiaryText(){ + public String getTertiaryText() { return tertiaryText; } @@ -406,7 +406,7 @@ public class MenuCell implements Cloneable { * * @return the cell's secondaryArtwork */ - public SdlArtwork getSecondaryArtwork(){ + public SdlArtwork getSecondaryArtwork() { return secondaryArtwork; } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 7db1a85b9..994c14ad8 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -48,6 +48,8 @@ import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.send import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.transferCellIDsFromCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.transferCellListenersFromCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.windowCapabilitySupportsPrimaryImage; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.windowCapabilitySupportsSecondaryImage; import com.livio.taskmaster.Task; import com.smartdevicelink.managers.CompletionListener; @@ -198,7 +200,7 @@ class MenuReplaceOperation extends Task { } private void uploadMenuArtworks(final CompletionListener listener) { - List<SdlArtwork> artworksToBeUploaded = new ArrayList<>(findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), windowCapability)); + List<SdlArtwork> artworksToBeUploaded = new ArrayList<>(findAllArtworksToBeUploadedFromCells(internalInterface.get(), updatedMenu, fileManager.get(), windowCapability)); if (artworksToBeUploaded.isEmpty()) { listener.onComplete(true); return; @@ -373,10 +375,10 @@ class MenuReplaceOperation extends Task { MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; // RPCs for cells on the main menu level. They could be AddCommands or AddSubMenus depending on whether the cell has child cells or not. - final List<RPCRequest> mainMenuCommands = mainMenuCommandsForCells(addMenuCells, fileManager.get(), fullMenu, windowCapability, defaultSubmenuLayout); + final List<RPCRequest> mainMenuCommands = mainMenuCommandsForCells(internalInterface.get(), addMenuCells, fileManager.get(), fullMenu, windowCapability, defaultSubmenuLayout); // RPCs for cells on the second menu level (one level deep). They could be AddCommands or AddSubMenus. - final List<RPCRequest> subMenuCommands = subMenuCommandsForCells(addMenuCells, fileManager.get(), windowCapability, defaultSubmenuLayout); + final List<RPCRequest> subMenuCommands = subMenuCommandsForCells(internalInterface.get(), addMenuCells, fileManager.get(), windowCapability, defaultSubmenuLayout); sendRPCs(mainMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { @Override @@ -465,12 +467,14 @@ class MenuReplaceOperation extends Task { // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI cell.setVoiceCommands(null); - // Don't check ImageFieldName.subMenuIcon because it was added in 7.0 when the feature was added in 5.0. - // Just assume that if cmdIcon is not available, the submenu icon is not either. - if (!hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon)) { + if (!windowCapabilitySupportsPrimaryImage(internalInterface.get(), windowCapability, cell)) { cell.setIcon(null); } + if (!windowCapabilitySupportsSecondaryImage(windowCapability, cell)) { + cell.setSecondaryArtwork(null); + } + // Check for subMenu fields supported if (cell.isSubMenuCell()) { if (!hasTextFieldOfName(windowCapability, TextFieldName.menuSubMenuSecondaryText)) { @@ -479,9 +483,6 @@ class MenuReplaceOperation extends Task { if (!hasTextFieldOfName(windowCapability, TextFieldName.menuSubMenuTertiaryText)) { cell.setTertiaryText(null); } - if (!hasImageFieldOfName(windowCapability, ImageFieldName.menuSubMenuSecondaryImage)) { - cell.setSecondaryArtwork(null); - } cell.setSubCells(cellsWithRemovedPropertiesFromCells(cell.getSubCells(), windowCapability)); } else { if (!hasTextFieldOfName(windowCapability, TextFieldName.menuCommandSecondaryText)) { @@ -490,9 +491,6 @@ class MenuReplaceOperation extends Task { if (!hasTextFieldOfName(windowCapability, TextFieldName.menuCommandTertiaryText)) { cell.setTertiaryText(null); } - if (!hasImageFieldOfName(windowCapability, ImageFieldName.menuCommandSecondaryImage)) { - cell.setSecondaryArtwork(null); - } } } return removePropertiesClone; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 210b24ba9..2e9711dda 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -53,6 +53,7 @@ import com.smartdevicelink.proxy.rpc.enums.MenuLayout; import com.smartdevicelink.proxy.rpc.enums.TextFieldName; import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import com.smartdevicelink.util.DebugTool; +import com.smartdevicelink.util.Version; import java.util.ArrayList; import java.util.HashMap; @@ -119,7 +120,7 @@ class MenuReplaceUtilities { } } - static Set<SdlArtwork> findAllArtworksToBeUploadedFromCells(List<MenuCell> cells, FileManager fileManager, WindowCapability windowCapability) { + static Set<SdlArtwork> findAllArtworksToBeUploadedFromCells(ISdl internalInterface, List<MenuCell> cells, FileManager fileManager, WindowCapability windowCapability) { // Make sure we can use images in the menus if (!hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon)) { return new HashSet<>(); @@ -128,30 +129,59 @@ class MenuReplaceUtilities { Set<SdlArtwork> artworks = new HashSet<>(); for (MenuCell cell : cells) { if (fileManager != null) { - if (fileManager.fileNeedsUpload(cell.getIcon())) { + if (windowCapabilitySupportsPrimaryImage(internalInterface, windowCapability, cell) && fileManager.fileNeedsUpload(cell.getIcon())) { artworks.add(cell.getIcon()); } - if (hasImageFieldOfName(windowCapability, ImageFieldName.menuCommandSecondaryImage) && fileManager.fileNeedsUpload(cell.getSecondaryArtwork())) { + if (windowCapabilitySupportsSecondaryImage(windowCapability, cell) && fileManager.fileNeedsUpload(cell.getSecondaryArtwork())) { artworks.add(cell.getSecondaryArtwork()); } } if (cell.isSubMenuCell() && !cell.getSubCells().isEmpty()) { - artworks.addAll(findAllArtworksToBeUploadedFromCells(cell.getSubCells(), fileManager, windowCapability)); + artworks.addAll(findAllArtworksToBeUploadedFromCells(internalInterface, cell.getSubCells(), fileManager, windowCapability)); } } return artworks; } - // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image - static boolean shouldCellIncludePrimaryImageFromCell(MenuCell cell, FileManager fileManager, WindowCapability windowCapability) { - boolean supportsImage = cell.isSubMenuCell() ? hasImageFieldOfName(windowCapability, ImageFieldName.subMenuIcon) : hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon); + static boolean windowCapabilitySupportsPrimaryImage(ISdl internalInterface, WindowCapability windowCapability, MenuCell cell) { + boolean supportsImage; + if (cell.isSubMenuCell()) { + if (isRPCVersionBetween5And7(internalInterface) && hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon)) { + supportsImage = true; + } else { + supportsImage = hasImageFieldOfName(windowCapability, ImageFieldName.subMenuIcon); + } + } else { + supportsImage = hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon); + } + return supportsImage; + } + + static boolean isRPCVersionBetween5And7(ISdl internalInterface) { + if (internalInterface != null && internalInterface.getSdlMsgVersion() != null) { + Version headUnitRPCVersion = new Version(internalInterface.getSdlMsgVersion()); + Version minRPCVersion = new Version(5, 0, 0); + Version maxRPCVersion = new Version(7, 0, 0); + // If RPC version is >= 5.0 && < 7.0 + return (headUnitRPCVersion.isNewerThan(minRPCVersion) == 0 || headUnitRPCVersion.isBetween(minRPCVersion, maxRPCVersion) == 1); + } + return false; + } + + static boolean windowCapabilitySupportsSecondaryImage(WindowCapability windowCapability, MenuCell cell) { + return cell.isSubMenuCell() ? hasImageFieldOfName(windowCapability, ImageFieldName.menuSubMenuSecondaryImage) : hasImageFieldOfName(windowCapability, ImageFieldName.menuCommandSecondaryImage); + } + + // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image + static boolean shouldCellIncludePrimaryImageFromCell(ISdl internalInterface, MenuCell cell, FileManager fileManager, WindowCapability windowCapability) { + boolean supportsImage = windowCapabilitySupportsPrimaryImage(internalInterface, windowCapability, cell); return cell.getIcon() != null && supportsImage && (fileManager.hasUploadedFile(cell.getIcon()) || cell.getIcon().isStaticIcon()); } // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image static boolean shouldCellIncludeSecondaryImageFromCell(MenuCell cell, FileManager fileManager, WindowCapability windowCapability) { - boolean supportsImage = cell.isSubMenuCell() ? hasImageFieldOfName(windowCapability, ImageFieldName.menuSubMenuSecondaryImage) : hasImageFieldOfName(windowCapability, ImageFieldName.menuCommandSecondaryImage); + boolean supportsImage = windowCapabilitySupportsSecondaryImage(windowCapability, cell); return cell.getSecondaryArtwork() != null && supportsImage && (fileManager.hasUploadedFile(cell.getSecondaryArtwork()) || cell.getSecondaryArtwork().isStaticIcon()); } @@ -193,7 +223,7 @@ class MenuReplaceUtilities { return deletes; } - static List<RPCRequest> mainMenuCommandsForCells(List<MenuCell> cells, FileManager fileManager, List<MenuCell> menu, WindowCapability windowCapability, MenuLayout defaultSubmenuLayout) { + static List<RPCRequest> mainMenuCommandsForCells(ISdl internalInterface, List<MenuCell> cells, FileManager fileManager, List<MenuCell> menu, WindowCapability windowCapability, MenuLayout defaultSubmenuLayout) { List<RPCRequest> commands = new ArrayList<>(); // We need the index to use it as position so we will use this type of loop @@ -203,9 +233,9 @@ class MenuReplaceUtilities { MenuCell addCell = cells.get(updateCellsIndex); if (mainCell.equals(addCell)) { if (addCell.isSubMenuCell()) { - commands.add(subMenuCommandForMenuCell(addCell, fileManager, windowCapability, menuInteger, defaultSubmenuLayout)); + commands.add(subMenuCommandForMenuCell(internalInterface, addCell, fileManager, windowCapability, menuInteger, defaultSubmenuLayout)); } else { - commands.add(commandForMenuCell(addCell, fileManager, windowCapability, menuInteger)); + commands.add(commandForMenuCell(internalInterface, addCell, fileManager, windowCapability, menuInteger)); } break; } @@ -214,36 +244,36 @@ class MenuReplaceUtilities { return commands; } - static List<RPCRequest> subMenuCommandsForCells(List<MenuCell> cells, FileManager fileManager, WindowCapability windowCapability, MenuLayout defaultSubmenuLayout) { + static List<RPCRequest> subMenuCommandsForCells(ISdl internalInterface, List<MenuCell> cells, FileManager fileManager, WindowCapability windowCapability, MenuLayout defaultSubmenuLayout) { List<RPCRequest> commands = new ArrayList<>(); for (MenuCell cell : cells) { if (cell.isSubMenuCell() && !cell.getSubCells().isEmpty()) { - commands.addAll(allCommandsForCells(cell.getSubCells(), fileManager, windowCapability, defaultSubmenuLayout)); + commands.addAll(allCommandsForCells(internalInterface, cell.getSubCells(), fileManager, windowCapability, defaultSubmenuLayout)); } } return commands; } - static List<RPCRequest> allCommandsForCells(List<MenuCell> cells, FileManager fileManager, WindowCapability windowCapability, MenuLayout defaultSubmenuLayout) { + static List<RPCRequest> allCommandsForCells(ISdl internalInterface, List<MenuCell> cells, FileManager fileManager, WindowCapability windowCapability, MenuLayout defaultSubmenuLayout) { List<RPCRequest> commands = new ArrayList<>(); for (int cellIndex = 0; cellIndex < cells.size(); cellIndex++) { MenuCell cell = cells.get(cellIndex); if (cell.isSubMenuCell()) { - commands.add(subMenuCommandForMenuCell(cell, fileManager, windowCapability, cellIndex, defaultSubmenuLayout)); + commands.add(subMenuCommandForMenuCell(internalInterface, cell, fileManager, windowCapability, cellIndex, defaultSubmenuLayout)); // Recursively grab the commands for all the sub cells if (!cell.getSubCells().isEmpty()) { - commands.addAll(allCommandsForCells(cell.getSubCells(), fileManager, windowCapability, defaultSubmenuLayout)); + commands.addAll(allCommandsForCells(internalInterface, cell.getSubCells(), fileManager, windowCapability, defaultSubmenuLayout)); } } else { - commands.add(commandForMenuCell(cell, fileManager, windowCapability, cellIndex)); + commands.add(commandForMenuCell(internalInterface, cell, fileManager, windowCapability, cellIndex)); } } return commands; } - static AddCommand commandForMenuCell(MenuCell cell, FileManager fileManager, WindowCapability windowCapability, int position) { + static AddCommand commandForMenuCell(ISdl internalInterface, MenuCell cell, FileManager fileManager, WindowCapability windowCapability, int position) { AddCommand command = new AddCommand(cell.getCellId()); MenuParams params = new MenuParams(cell.getUniqueTitle()); @@ -258,7 +288,7 @@ class MenuReplaceUtilities { } else { command.setVrCommands(null); } - boolean shouldCellIncludePrimaryImage = cell.getIcon() != null && shouldCellIncludePrimaryImageFromCell(cell, fileManager, windowCapability); + boolean shouldCellIncludePrimaryImage = cell.getIcon() != null && shouldCellIncludePrimaryImageFromCell(internalInterface, cell, fileManager, windowCapability); command.setCmdIcon(shouldCellIncludePrimaryImage ? cell.getIcon().getImageRPC() : null); boolean shouldCellIncludeSecondaryImage = cell.getSecondaryArtwork() != null && shouldCellIncludeSecondaryImageFromCell(cell, fileManager, windowCapability); @@ -267,8 +297,8 @@ class MenuReplaceUtilities { return command; } - static AddSubMenu subMenuCommandForMenuCell(MenuCell cell, FileManager fileManager, WindowCapability windowCapability, int position, MenuLayout defaultSubmenuLayout) { - boolean shouldCellIncludePrimaryImage = cell.getIcon() != null && cell.getIcon().getImageRPC() != null && shouldCellIncludePrimaryImageFromCell(cell, fileManager, windowCapability); + static AddSubMenu subMenuCommandForMenuCell(ISdl internalInterface, MenuCell cell, FileManager fileManager, WindowCapability windowCapability, int position, MenuLayout defaultSubmenuLayout) { + boolean shouldCellIncludePrimaryImage = cell.getIcon() != null && cell.getIcon().getImageRPC() != null && shouldCellIncludePrimaryImageFromCell(internalInterface, cell, fileManager, windowCapability); Image icon = (shouldCellIncludePrimaryImage ? cell.getIcon().getImageRPC() : null); boolean shouldCellIncludeSecondaryImage = cell.getSecondaryArtwork() != null && cell.getSecondaryArtwork().getImageRPC() != null && shouldCellIncludeSecondaryImageFromCell(cell, fileManager, windowCapability); Image secondaryIcon = (shouldCellIncludeSecondaryImage ? cell.getSecondaryArtwork().getImageRPC() : null); @@ -421,4 +451,4 @@ class MenuReplaceUtilities { } }); } -}
\ No newline at end of file +} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperation.java index 0650ab388..1144e1479 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperation.java @@ -206,7 +206,7 @@ class VoiceCommandUpdateOperation extends Task { private void sendCurrentVoiceCommands(final CompletionListener listener) { - List<VoiceCommand> voiceCommandsToAdd = voiceCommandsInListNotInSecondList(pendingVoiceCommands, oldVoiceCommands); + List<VoiceCommand> voiceCommandsToAdd = voiceCommandsInListNotInSecondList(pendingVoiceCommands, oldVoiceCommands); if (voiceCommandsToAdd.size() == 0) { if (listener != null) { diff --git a/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java b/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java index f7dd797d0..a8c739750 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java +++ b/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java @@ -1239,7 +1239,10 @@ public class SdlProtocolBase { } } - if ((getTransportForSession(SessionType.RPC) != null && disconnectedTransport.equals(getTransportForSession(SessionType.RPC))) || disconnectedTransport.equals(connectedPrimaryTransport)) { + if ((getTransportForSession(SessionType.RPC) != null && disconnectedTransport.equals(getTransportForSession(SessionType.RPC))) + || disconnectedTransport.equals(connectedPrimaryTransport) + || connectedTransports == null + || connectedTransports.isEmpty()) { //Primary transport has been disconnected. Let's check if we can recover. //transportTypes.remove(type); boolean primaryTransportAvailable = false; diff --git a/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java b/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java index c7bc1326d..b38c5304a 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java +++ b/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java @@ -16,7 +16,7 @@ public class SecurityQueryPayload { private SecurityQueryID _securityQueryID; private int _correlationID; private int _jsonSize; - private SecurityQueryErrorCode _errorCode; + private int _bulkDataSize; private byte[] _jsonData = null; private byte[] _bulkData = null; @@ -51,11 +51,6 @@ public class SecurityQueryPayload { 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)) { @@ -66,12 +61,7 @@ public class SecurityQueryPayload { //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]; - } + byte[] _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); } @@ -84,18 +74,28 @@ public class SecurityQueryPayload { return msg; } - public byte[] assembleHeaderBytes() { + public byte[] assembleBinaryData() { // 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; + byte[] header = new byte[SECURITY_QUERY_HEADER_SIZE]; + header[0] = _securityQueryType.getValue(); + System.arraycopy(_securityQueryID.getValue(), 0, header, 1, 3); + System.arraycopy(BitConverter.intToByteArray(_correlationID), 0, header, 4, 4); + System.arraycopy(BitConverter.intToByteArray(_jsonSize), 0, header, 8, 4); + + int size = _jsonSize + _bulkDataSize + SECURITY_QUERY_HEADER_SIZE; + byte[] dataOut = new byte[size]; + System.arraycopy(header, 0, dataOut, 0, SECURITY_QUERY_HEADER_SIZE); + if (_jsonData != null) { + System.arraycopy(_jsonData, 0, dataOut, SECURITY_QUERY_HEADER_SIZE, _jsonSize); + } + if (_bulkData != null) { + System.arraycopy(_bulkData, 0, dataOut, SECURITY_QUERY_HEADER_SIZE + _jsonSize, _bulkDataSize); + } + return dataOut; } public SecurityQueryType getQueryType() { @@ -126,16 +126,16 @@ public class SecurityQueryPayload { return _jsonSize; } - public void setJsonSize(int _jsonSize) { + private void setJsonSize(int _jsonSize) { this._jsonSize = _jsonSize; } - public SecurityQueryErrorCode getErrorCode() { - return _errorCode; + public int getBulkDataSize() { + return _bulkDataSize; } - public void setErrorCode(SecurityQueryErrorCode _errorCode) { - this._errorCode = _errorCode; + private void setBulkDataSize(int _bulkDataSize) { + this._bulkDataSize = _bulkDataSize; } public byte[] getJsonData() { @@ -143,6 +143,12 @@ public class SecurityQueryPayload { } public void setJsonData(byte[] _jsonData) { + if (_jsonData == null) { + this._jsonSize = 0; + this._jsonData = null; + return; + } + this._jsonSize = _jsonData.length; this._jsonData = new byte[this._jsonSize]; System.arraycopy(_jsonData, 0, this._jsonData, 0, _jsonSize); } @@ -152,6 +158,13 @@ public class SecurityQueryPayload { } public void setBulkData(byte[] _bulkData) { - this._bulkData = _bulkData; + if (_bulkData == null) { + this._bulkDataSize = 0; + this._bulkData = null; + return; + } + this._bulkDataSize = _bulkData.length; + this._bulkData = new byte[this._bulkDataSize]; + System.arraycopy(_bulkData, 0, this._bulkData, 0, _bulkDataSize); } } diff --git a/base/src/main/java/com/smartdevicelink/proxy/RPCStruct.java b/base/src/main/java/com/smartdevicelink/proxy/RPCStruct.java index 1db39f2e9..8072a4b23 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/RPCStruct.java +++ b/base/src/main/java/com/smartdevicelink/proxy/RPCStruct.java @@ -117,7 +117,7 @@ public class RPCStruct implements Cloneable { }
/**
- * This method should clean the the RPC to make sure it is compliant with the spec.
+ * This method should clean the RPC to make sure it is compliant with the spec.
* <br><br><b> NOTE:</b> Super needs to be called at the END of the method
*
* @param rpcVersion the rpc spec version that has been negotiated. If value is null the
@@ -270,7 +270,7 @@ public class RPCStruct implements Cloneable { return customObject;
} catch (Exception e) {
- DebugTool.logError(TAG,"Error attempting to format an object from a Hashtable", e);
+ DebugTool.logError(TAG, "Error attempting to format an object from a Hashtable", e);
}
} else if (obj instanceof List<?>) {
List<?> list = (List<?>) obj;
@@ -302,7 +302,7 @@ public class RPCStruct implements Cloneable { }
newList.add(customObject);
} catch (Exception e) {
- DebugTool.logError(TAG,"Error attempting to format object from list of Hashtables", e);
+ DebugTool.logError(TAG, "Error attempting to format object from list of Hashtables", e);
return null;
}
}
@@ -336,15 +336,15 @@ public class RPCStruct implements Cloneable { try {
valueForString = tClass.getDeclaredMethod("valueForString", String.class);
} catch (NoSuchMethodException e) {
- DebugTool.logError(TAG,"Error attempting to find valueForString method in class", e);
+ DebugTool.logError(TAG, "Error attempting to find valueForString method in class", e);
}
if (valueForString != null) {
try {
return valueForString.invoke(null, (String) s);
} catch (IllegalAccessException e) {
- DebugTool.logError(TAG,"Illegal access while using reflection to get enum from string", e);
+ DebugTool.logError(TAG, "Illegal access while using reflection to get enum from string", e);
} catch (InvocationTargetException e) {
- DebugTool.logError(TAG,"Error attempting to use method from reflection to get enum from string", e);
+ DebugTool.logError(TAG, "Error attempting to use method from reflection to get enum from string", e);
}
}
return null;
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/AddCommand.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/AddCommand.java index 3458e361a..311279eb8 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/AddCommand.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/AddCommand.java @@ -176,7 +176,7 @@ public class AddCommand extends RPCRequest { }
/**
- * <p>Sets an Unique Command ID that identifies the command. Is returned in an
+ * <p>Sets a Unique Command ID that identifies the command. Is returned in an
* <i>{@linkplain OnCommand}</i> notification to identify the command
* selected by the user</p>
*
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/GetVehicleDataResponse.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/GetVehicleDataResponse.java index 33ebf324f..c7b429075 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/GetVehicleDataResponse.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/GetVehicleDataResponse.java @@ -110,6 +110,7 @@ public class GetVehicleDataResponse extends RPCResponse { * @since SmartDeviceLink 7.1.0
*/
public static final String KEY_SEAT_OCCUPANCY = "seatOccupancy";
+
/**
* Constructs a new GetVehicleDataResponse object
*/
@@ -648,6 +649,7 @@ public class GetVehicleDataResponse extends RPCResponse { public ClimateData getClimateData() {
return (ClimateData) getObject(ClimateData.class, KEY_CLIMATE_DATA);
}
+
/**
* Sets the seatOccupancy.
*
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/ScrollableMessage.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/ScrollableMessage.java index 019b3d55f..e5e96d7ef 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/ScrollableMessage.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/ScrollableMessage.java @@ -70,7 +70,7 @@ import java.util.List; * <tr>
* <td>timeout</td>
* <td>Integer</td>
- * <td>App defined timeout. Indicates how long of a timeout from the last action (i.e. scrolling message resets timeout).</td>
+ * <td>App defined timeout. Indicates how long of a timeout in milliseconds from the last action (i.e. scrolling message resets timeout).</td>
* <td>N</td>
* <td>minValue=1000; maxValue=65535; defValue=30000</td>
* <td>SmartDevice Link 1.0 </td>
@@ -166,10 +166,10 @@ public class ScrollableMessage extends RPCRequest { }
/**
- * Sets an App defined timeout. Indicates how long of a timeout from the
+ * Sets an App defined timeout. Indicates how long of a timeout in milliseconds from the
* last action
*
- * @param timeout an Integer value representing an App defined timeout
+ * @param timeout an Integer value representing an App defined timeout in milliseconds
* <p></p>
* <b>Notes</b>:Minval=0; Maxval=65535;Default=30000
*/
@@ -179,9 +179,9 @@ public class ScrollableMessage extends RPCRequest { }
/**
- * Gets an App defined timeout
+ * Gets an App defined timeout in milliseconds
*
- * @return Integer -an Integer value representing an App defined timeout
+ * @return Integer -an Integer value representing an App defined timeout in milliseconds
*/
public Integer getTimeout() {
return getInteger(KEY_TIMEOUT);
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/Slider.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/Slider.java index fae2a8165..152df6f4d 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/Slider.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/Slider.java @@ -95,7 +95,7 @@ import java.util.List; * <tr>
* <td>timeout</td>
* <td>String</td>
- * <td>App defined timeout. Indicates how long of a timeout from the last action (i.e. sliding control resets timeout). If omitted, the value is set to 10000.</td>
+ * <td>App defined timeout. Indicates how long of a timeout in milliseconds from the last action (i.e. sliding control resets timeout). If omitted, the value is set to 10000.</td>
* <td>N</td>
* <td>Minvalue=0; Maxvalue=65535; Defvalue= 10000</td>
* <td>SmartDeviceLink 2.0</td>
@@ -255,9 +255,9 @@ public class Slider extends RPCRequest { }
/**
- * Sets an App defined timeout
+ * Sets an App defined timeout in milliseconds
*
- * @param timeout an Integer value representing an App defined timeout
+ * @param timeout an Integer value representing an App defined timeout in milliseconds
* <p></p>
* <b>Notes: </b>Minvalue=0; Maxvalue=65535; Defvalue=10000
*/
@@ -267,9 +267,9 @@ public class Slider extends RPCRequest { }
/**
- * Gets an App defined timeout
+ * Gets an App defined timeout in milliseconds
*
- * @return Integer -an Integer value representing an App defined timeout
+ * @return Integer -an Integer value representing an App defined timeout in milliseconds
*/
public Integer getTimeout() {
return getInteger(KEY_TIMEOUT);
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/SubscribeVehicleDataResponse.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/SubscribeVehicleDataResponse.java index 7a268f0f3..05753b8ab 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/SubscribeVehicleDataResponse.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/SubscribeVehicleDataResponse.java @@ -102,6 +102,7 @@ public class SubscribeVehicleDataResponse extends RPCResponse { * @since SmartDeviceLink 7.1.0
*/
public static final String KEY_SEAT_OCCUPANCY = "seatOccupancy";
+
/**
* Constructs a new SubscribeVehicleDataResponse object
*/
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/TireStatus.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/TireStatus.java index 68c47cf50..bd7850480 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/TireStatus.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/TireStatus.java @@ -159,6 +159,7 @@ public class TireStatus extends RPCStruct { * @since SmartDeviceLink 2.0.0
*/
public static final String KEY_RIGHT_REAR = "rightRear";
+
public TireStatus() {
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/UnsubscribeVehicleDataResponse.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/UnsubscribeVehicleDataResponse.java index 114a1971a..9cb5421a8 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/UnsubscribeVehicleDataResponse.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/UnsubscribeVehicleDataResponse.java @@ -102,6 +102,7 @@ public class UnsubscribeVehicleDataResponse extends RPCResponse { * @since SmartDeviceLink 7.1.0
*/
public static final String KEY_SEAT_OCCUPANCY = "seatOccupancy";
+
/**
* Constructs a new UnsubscribeVehicleDataResponse object
*/
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/VideoStreamingCapability.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/VideoStreamingCapability.java index 6e1b6abbf..bc879f89b 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/VideoStreamingCapability.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/VideoStreamingCapability.java @@ -197,7 +197,7 @@ public class VideoStreamingCapability extends RPCStruct { * @since SmartDeviceLink 7.1.0 */ @SuppressWarnings("unchecked") - public List<VideoStreamingCapability> getAdditionalVideoStreamingCapabilities(){ + public List<VideoStreamingCapability> getAdditionalVideoStreamingCapabilities() { return (List<VideoStreamingCapability>) getObject(VideoStreamingCapability.class, KEY_ADDITIONAL_VIDEO_STREAMING_CAPABILITIES); } diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/AppCapabilityType.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/AppCapabilityType.java index 79969528c..7b76d3e5f 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/AppCapabilityType.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/AppCapabilityType.java @@ -46,9 +46,9 @@ public enum AppCapabilityType { * @return AppCapabilityType */ public static AppCapabilityType valueForString(String value) { - try{ + try { return valueOf(value); - }catch(Exception e){ + } catch (Exception e) { return null; } } diff --git a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java index 5fcc8d8ca..a33d3eb04 100644 --- a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java +++ b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java @@ -44,6 +44,7 @@ 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.SecurityQueryErrorCode; import com.smartdevicelink.protocol.enums.SecurityQueryID; import com.smartdevicelink.protocol.enums.SecurityQueryType; import com.smartdevicelink.protocol.enums.SessionType; @@ -61,7 +62,9 @@ import com.smartdevicelink.util.DebugTool; import com.smartdevicelink.util.SystemInfo; import com.smartdevicelink.util.Version; -import java.util.ArrayList; +import org.json.JSONException; +import org.json.JSONObject; + import java.util.HashMap; import java.util.List; import java.util.ListIterator; @@ -217,8 +220,8 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ // 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()); + if (receivedHeader.getBulkData() != null && receivedHeader.getBulkDataSize() == 1) { + DebugTool.logError(TAG, "Security Query module internal error: " + SecurityQueryErrorCode.valueOf(receivedHeader.getBulkData()[0]).getName()); } else { DebugTool.logError(TAG, "Security Query module error: No information provided"); } @@ -237,29 +240,28 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ iNumBytes = sdlSecurity.runHandshake(data, dataToRead); - // Assemble a security query payload header for our response - SecurityQueryPayload responseHeader = new SecurityQueryPayload(); - - byte[] returnBytes; + ProtocolMessage protocolMessage; 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); - returnBytes = new byte[12]; + protocolMessage = serverSecurityFailedMessageWithClientMessageHeader(msg.getCorrID()); } else { - responseHeader.setQueryID(SecurityQueryID.SEND_HANDSHAKE_DATA); - responseHeader.setQueryType(SecurityQueryType.RESPONSE); - responseHeader.setCorrelationID(msg.getCorrID()); - responseHeader.setJsonSize(0); - returnBytes = new byte[iNumBytes + 12]; - System.arraycopy(dataToRead, 0, returnBytes, 12, iNumBytes); + protocolMessage = serverSecurityHandshakeMessageWithData(msg.getCorrID(), dataToRead); } + //sdlSecurity.hs(); + + sendMessage(protocolMessage); + } + + private ProtocolMessage serverSecurityHandshakeMessageWithData(int correlationId, byte[] bulkData) { + SecurityQueryPayload responseHeader = new SecurityQueryPayload(); + responseHeader.setQueryID(SecurityQueryID.SEND_HANDSHAKE_DATA); + responseHeader.setQueryType(SecurityQueryType.RESPONSE); + responseHeader.setCorrelationID(correlationId); + responseHeader.setBulkData(bulkData); + responseHeader.setJsonData(null); - System.arraycopy(responseHeader.assembleHeaderBytes(), 0, returnBytes, 0, 12); + byte[] returnBytes = responseHeader.assembleBinaryData(); ProtocolMessage protocolMessage = new ProtocolMessage(); protocolMessage.setSessionType(SessionType.CONTROL); @@ -268,9 +270,40 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ protocolMessage.setVersion((byte) sdlProtocol.getProtocolVersion().getMajor()); protocolMessage.setSessionID((byte) this.sessionId); - //sdlSecurity.hs(); + return protocolMessage; + } - sendMessage(protocolMessage); + private ProtocolMessage serverSecurityFailedMessageWithClientMessageHeader(int correlationId) { + SecurityQueryPayload responseHeader = new SecurityQueryPayload(); + responseHeader.setQueryID(SecurityQueryID.SEND_INTERNAL_ERROR); + responseHeader.setQueryType(SecurityQueryType.NOTIFICATION); + responseHeader.setCorrelationID(correlationId); + byte[] jsonData; + JSONObject jsonObject = new JSONObject(); + try { + jsonObject.put("id", SecurityQueryErrorCode.ERROR_UNKNOWN_INTERNAL_ERROR.getValue()); + jsonObject.put("text", SecurityQueryErrorCode.ERROR_UNKNOWN_INTERNAL_ERROR.getName()); + jsonData = jsonObject.toString().getBytes(); + } catch (JSONException e) { + DebugTool.logError(TAG, "JSON exception when constructing handshake error Notification"); + e.printStackTrace(); + jsonData = new byte[0]; + } + responseHeader.setJsonData(jsonData); + byte[] errorCode = new byte[1]; + errorCode[0] = SecurityQueryErrorCode.ERROR_UNKNOWN_INTERNAL_ERROR.getValue(); + responseHeader.setBulkData(errorCode); + + byte[] returnBytes = responseHeader.assembleBinaryData(); + + ProtocolMessage protocolMessage = new ProtocolMessage(); + protocolMessage.setSessionType(SessionType.CONTROL); + protocolMessage.setData(returnBytes); + protocolMessage.setFunctionID(0x01); + protocolMessage.setVersion((byte) sdlProtocol.getProtocolVersion().getMajor()); + protocolMessage.setSessionID((byte) this.sessionId); + + return protocolMessage; } /** diff --git a/base/src/main/java/com/smartdevicelink/streaming/video/VideoStreamingParameters.java b/base/src/main/java/com/smartdevicelink/streaming/video/VideoStreamingParameters.java index 7a2fe7b78..77e0ffb0a 100644 --- a/base/src/main/java/com/smartdevicelink/streaming/video/VideoStreamingParameters.java +++ b/base/src/main/java/com/smartdevicelink/streaming/video/VideoStreamingParameters.java @@ -98,13 +98,13 @@ public class VideoStreamingParameters { @Deprecated public VideoStreamingParameters(int displayDensity, int frameRate, int bitrate, int interval, ImageResolution resolution, VideoStreamingFormat format) { - this.displayDensity = displayDensity; - this.frameRate = frameRate; - this.bitrate = bitrate; - this.interval = interval; - this.resolution = resolution; - this.format = format; - this.stableFrameRate = true; + this.displayDensity = displayDensity; + this.frameRate = frameRate; + this.bitrate = bitrate; + this.interval = interval; + this.resolution = resolution; + this.format = format; + this.stableFrameRate = true; } /** @@ -178,7 +178,7 @@ public class VideoStreamingParameters { if (!params.additionalCapabilities.isEmpty()) { this.additionalCapabilities = params.additionalCapabilities; } - this.stableFrameRate = params.stableFrameRate; + this.stableFrameRate = params.stableFrameRate; } } @@ -304,17 +304,21 @@ public class VideoStreamingParameters { return resolution; } - public boolean isStableFrameRate() { - return stableFrameRate; - } + public boolean isStableFrameRate() { + return stableFrameRate; + } - public void setStableFrameRate(boolean isStable) { - stableFrameRate = isStable; - } + public void setStableFrameRate(boolean isStable) { + stableFrameRate = isStable; + } - public double getScale() { return scale; } + public double getScale() { + return scale; + } - public double getPreferredDiagonal() { return preferredDiagonal; } + public double getPreferredDiagonal() { + return preferredDiagonal; + } @Override public String toString() { diff --git a/base/src/main/java/com/smartdevicelink/transport/SdlPsm.java b/base/src/main/java/com/smartdevicelink/transport/SdlPsm.java index dc8fd3bbc..24593c32d 100644 --- a/base/src/main/java/com/smartdevicelink/transport/SdlPsm.java +++ b/base/src/main/java/com/smartdevicelink/transport/SdlPsm.java @@ -133,11 +133,11 @@ public class SdlPsm { //Log.trace(TAG,"Frame Info: " + controlFrameInfo); switch (frameType) { case SdlPacket.FRAME_TYPE_CONTROL: - /*if(frameInfo<FRAME_INFO_HEART_BEAT - || (frameInfo>FRAME_INFO_END_SERVICE_ACK - && (frameInfo!=FRAME_INFO_SERVICE_DATA_ACK || frameInfo!=FRAME_INFO_HEART_BEAT_ACK))){ - return ERROR_STATE; - }*/ //Although some bits are reserved...whatever + /*if(frameInfo<FRAME_INFO_HEART_BEAT + || (frameInfo>FRAME_INFO_END_SERVICE_ACK + && (frameInfo!=FRAME_INFO_SERVICE_DATA_ACK || frameInfo!=FRAME_INFO_HEART_BEAT_ACK))){ + return ERROR_STATE; + }*/ //Although some bits are reserved...whatever break; case SdlPacket.FRAME_TYPE_SINGLE: //Fall through since they are both the same case SdlPacket.FRAME_TYPE_FIRST: diff --git a/base/src/main/java/com/smartdevicelink/transport/SiphonServer.java b/base/src/main/java/com/smartdevicelink/transport/SiphonServer.java index 39871b777..66019908c 100644 --- a/base/src/main/java/com/smartdevicelink/transport/SiphonServer.java +++ b/base/src/main/java/com/smartdevicelink/transport/SiphonServer.java @@ -107,7 +107,7 @@ public class SiphonServer { try {
SiphonServer.closeServer();
} catch (IOException e) {
- DebugTool.logError(TAG,"Error while trying to close siphon server", e);
+ DebugTool.logError(TAG, "Error while trying to close siphon server", e);
}
return m_listenPort;
@@ -292,7 +292,7 @@ public class SiphonServer { try {
// blobSize = length(of message) + 1(size of direction indicator)
- // + 1 (size of msgVersionNumber) + 4 (size of timeStamp)
+ // + 1 (size of msgVersionNumber) + 4 (size of timeStamp)
int blobSize = length + 1 + 1 + 4;
siphonOutputStream.write(BitConverter.intToByteArray(blobSize));
diff --git a/base/src/main/java/com/smartdevicelink/transport/TransportConstants.java b/base/src/main/java/com/smartdevicelink/transport/TransportConstants.java index 79a055bc9..e671e2fa3 100644 --- a/base/src/main/java/com/smartdevicelink/transport/TransportConstants.java +++ b/base/src/main/java/com/smartdevicelink/transport/TransportConstants.java @@ -45,6 +45,8 @@ public class TransportConstants { public static final String FOREGROUND_EXTRA = "foreground"; public static final String CONFIRMED_SDL_DEVICE = "confirmed_sdl_device"; public static final String VEHICLE_INFO_EXTRA = "vehicle_info"; + public static final String CONNECTION_TYPE_EXTRA = "connection_type"; + public static final String PENDING_INTENT_EXTRA = "pending_intent"; public static final String BIND_LOCATION_PACKAGE_NAME_EXTRA = "BIND_LOCATION_PACKAGE_NAME_EXTRA"; public static final String BIND_LOCATION_CLASS_NAME_EXTRA = "BIND_LOCATION_CLASS_NAME_EXTRA"; diff --git a/base/src/main/java/com/smartdevicelink/util/FileUtls.java b/base/src/main/java/com/smartdevicelink/util/FileUtls.java index 747052415..387f06b5b 100644 --- a/base/src/main/java/com/smartdevicelink/util/FileUtls.java +++ b/base/src/main/java/com/smartdevicelink/util/FileUtls.java @@ -74,7 +74,7 @@ public class FileUtls { try { return Files.readAllBytes(file.toPath()); } catch (IOException e) { - DebugTool.logError(TAG,"Error trying to get file data", e); + DebugTool.logError(TAG, "Error trying to get file data", e); } } } diff --git a/javaEE/hello_sdl_java_ee/src/main/java/com/smartdevicelink/SdlService.java b/javaEE/hello_sdl_java_ee/src/main/java/com/smartdevicelink/SdlService.java index c261230a6..d0929219f 100644 --- a/javaEE/hello_sdl_java_ee/src/main/java/com/smartdevicelink/SdlService.java +++ b/javaEE/hello_sdl_java_ee/src/main/java/com/smartdevicelink/SdlService.java @@ -290,7 +290,7 @@ public class SdlService { } }); - MenuCell mainCell5 = new MenuCell("Clear the menu", null, null, null,null, null, new MenuSelectionListener() { + MenuCell mainCell5 = new MenuCell("Clear the menu", null, null, null, null, null, new MenuSelectionListener() { @Override public void onTriggered(TriggerSource trigger) { DebugTool.logInfo(TAG, "Clearing Menu. Source: " + trigger.toString()); @@ -353,7 +353,7 @@ public class SdlService { sdlManager.getScreenManager().presentAlert(alertView, new AlertCompletionListener() { @Override public void onComplete(boolean success, Integer tryAgainTime) { - DebugTool.logInfo(TAG, "Alert presented: "+ success); + DebugTool.logInfo(TAG, "Alert presented: " + success); } }); } diff --git a/javaEE/javaEE/build.gradle b/javaEE/javaEE/build.gradle index a97824e30..c891409a3 100644 --- a/javaEE/javaEE/build.gradle +++ b/javaEE/javaEE/build.gradle @@ -2,6 +2,7 @@ apply plugin: 'java-library' group 'com.smartdevicelink' version new File(projectDir.path, ('/../../VERSION')).text.trim() +ext { VERSION_NAME = "$project.version" } sourceCompatibility = 1.7 diff --git a/javaEE/javaEE/gradle.properties b/javaEE/javaEE/gradle.properties index a7af0f36b..4bb0ca403 100644 --- a/javaEE/javaEE/gradle.properties +++ b/javaEE/javaEE/gradle.properties @@ -1,6 +1,5 @@ GROUP=com.smartdevicelink POM_ARTIFACT_ID=sdl_java_ee -VERSION_NAME=5.3.1 POM_NAME=sdl_java_ee POM_PACKAGING=jar diff --git a/javaEE/javaEE/src/test/java/com/smartdevicelink/test/TestValues.java b/javaEE/javaEE/src/test/java/com/smartdevicelink/test/TestValues.java index d3efa2326..6ff8f26bb 100644 --- a/javaEE/javaEE/src/test/java/com/smartdevicelink/test/TestValues.java +++ b/javaEE/javaEE/src/test/java/com/smartdevicelink/test/TestValues.java @@ -5,9 +5,9 @@ import com.smartdevicelink.proxy.rpc.enums.FileType; import java.net.URI; public class TestValues { - public static final boolean GENERAL_BOOLEAN = true; - public static final byte[] GENERAL_BYTE_ARRAY = new byte[0]; - public static final String GENERAL_STRING = "test"; - public static final FileType GENERAL_FILETYPE = FileType.BINARY; - public static final URI GENERAL_URI = URI.create("http://www.google.com"); + public static final boolean GENERAL_BOOLEAN = true; + public static final byte[] GENERAL_BYTE_ARRAY = new byte[0]; + public static final String GENERAL_STRING = "test"; + public static final FileType GENERAL_FILETYPE = FileType.BINARY; + public static final URI GENERAL_URI = URI.create("http://www.google.com"); } diff --git a/javaSE/javaSE/build.gradle b/javaSE/javaSE/build.gradle index aa97ea53a..c20713e51 100644 --- a/javaSE/javaSE/build.gradle +++ b/javaSE/javaSE/build.gradle @@ -2,6 +2,7 @@ apply plugin: 'java-library' group 'com.smartdevicelink' version new File(projectDir.path, ('/../../VERSION')).text.trim() +ext { VERSION_NAME = "$project.version" } sourceCompatibility = 1.7 @@ -27,7 +28,7 @@ configurations { } dependencies { - testCompile group: 'junit', name: 'junit', version: '4.12' + testImplementation group: 'junit', name: 'junit', version: '4.12' extraLibs fileTree(dir: 'libs', include: ['*.jar']) extraLibs 'org.mongodb:bson:4.0.5' extraLibs 'androidx.annotation:annotation:1.1.0' diff --git a/javaSE/javaSE/gradle.properties b/javaSE/javaSE/gradle.properties index f2a4bb0f9..e8c9b58c6 100644 --- a/javaSE/javaSE/gradle.properties +++ b/javaSE/javaSE/gradle.properties @@ -1,6 +1,5 @@ GROUP=com.smartdevicelink POM_ARTIFACT_ID=sdl_java_se -VERSION_NAME=5.3.1 POM_NAME=sdl_java_se POM_PACKAGING=jar diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java index 9b4ce8fca..c2ed98ba6 100644 --- a/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java +++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java @@ -32,5 +32,5 @@ package com.smartdevicelink; // THIS FILE IS AUTO GENERATED, DO NOT MODIFY!! public final class BuildConfig { - public static final String VERSION_NAME = "5.3.1"; + public static final String VERSION_NAME = "5.4.0"; }
\ No newline at end of file diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java index f95639ca6..f08f41f58 100644 --- a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java +++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java @@ -157,6 +157,12 @@ public class SdlManager extends BaseSdlManager { @Override public void dispose() { + int state = getState(); + if (state == BaseSubManager.SHUTDOWN || state == BaseSubManager.ERROR) { + DebugTool.logInfo(TAG, "SdlManager already disposed"); + return; + } + if (this.permissionManager != null) { this.permissionManager.dispose(); } @@ -194,6 +200,7 @@ public class SdlManager extends BaseSdlManager { super(appId, appName, listener); } } + private ISdl _internalInterface = new ISdl() { @Override public void start() { @@ -202,7 +209,7 @@ public class SdlManager extends BaseSdlManager { @Override public void stop() { - lifecycleManager.getInternalInterface(SdlManager.this).start(); + lifecycleManager.getInternalInterface(SdlManager.this).stop(); } @Override @@ -252,7 +259,7 @@ public class SdlManager extends BaseSdlManager { @Override public boolean removeOnRPCNotificationListener(FunctionID notificationId, OnRPCNotificationListener listener) { - return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCNotificationListener(notificationId, listener); + return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCNotificationListener(notificationId, listener); } @Override @@ -262,7 +269,7 @@ public class SdlManager extends BaseSdlManager { @Override public boolean removeOnRPCRequestListener(FunctionID functionID, OnRPCRequestListener listener) { - return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCRequestListener(functionID, listener); + return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCRequestListener(functionID, listener); } @Override @@ -272,29 +279,29 @@ public class SdlManager extends BaseSdlManager { @Override public boolean removeOnRPCListener(FunctionID responseId, OnRPCListener listener) { - return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCListener(responseId, listener); + return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCListener(responseId, listener); } @Override public RegisterAppInterfaceResponse getRegisterAppInterfaceResponse() { - return lifecycleManager.getInternalInterface(SdlManager.this).getRegisterAppInterfaceResponse(); + return lifecycleManager.getInternalInterface(SdlManager.this).getRegisterAppInterfaceResponse(); } @Override public boolean isTransportForServiceAvailable(SessionType serviceType) { - return lifecycleManager.getInternalInterface(SdlManager.this).isTransportForServiceAvailable(serviceType); + return lifecycleManager.getInternalInterface(SdlManager.this).isTransportForServiceAvailable(serviceType); } @NonNull @Override public SdlMsgVersion getSdlMsgVersion() { - return lifecycleManager.getInternalInterface(SdlManager.this).getSdlMsgVersion(); + return lifecycleManager.getInternalInterface(SdlManager.this).getSdlMsgVersion(); } @NonNull @Override public Version getProtocolVersion() { - return lifecycleManager.getInternalInterface(SdlManager.this).getProtocolVersion(); + return lifecycleManager.getInternalInterface(SdlManager.this).getProtocolVersion(); } @Override @@ -309,12 +316,12 @@ public class SdlManager extends BaseSdlManager { @Override public Taskmaster getTaskmaster() { - return lifecycleManager.getInternalInterface(SdlManager.this).getTaskmaster(); + return lifecycleManager.getInternalInterface(SdlManager.this).getTaskmaster(); } @Override public SystemCapabilityManager getSystemCapabilityManager() { - return lifecycleManager.getInternalInterface(SdlManager.this).getSystemCapabilityManager(); + return lifecycleManager.getInternalInterface(SdlManager.this).getSystemCapabilityManager(); } @Override diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java index e29d34640..a23d1ad51 100644 --- a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java +++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java @@ -39,6 +39,8 @@ import com.smartdevicelink.proxy.rpc.enums.SdlDisconnectedReason; import com.smartdevicelink.session.SdlSession; import com.smartdevicelink.transport.BaseTransportConfig; +import static com.smartdevicelink.managers.BaseSubManager.SETTING_UP; + /** * The lifecycle manager creates a central point for all SDL session logic to converge. It should only be used by * the library itself. Usage outside the library is not permitted and will not be protected for in the future. @@ -57,7 +59,9 @@ public class LifecycleManager extends BaseLifecycleManager { @Override void cycle(SdlDisconnectedReason disconnectedReason) { - clean(); + clean(true); + transitionToState(SETTING_UP); + initialize(); if (session != null) { try { session.startSession(); diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java index addcee13d..15e92eb1a 100644 --- a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java +++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java @@ -4,6 +4,7 @@ import androidx.annotation.RestrictTo; import com.smartdevicelink.managers.ISdl; import com.smartdevicelink.managers.file.FileManager; + /** * <strong>AlertManager</strong> <br> * <p> |