summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Example Apps/Example ObjC/MenuManager.m18
-rw-r--r--Example Apps/Example Swift/MenuManager.swift22
-rw-r--r--SmartDeviceLink/SDLLifecycleManager.h2
-rw-r--r--SmartDeviceLink/SDLLifecycleManager.m40
-rw-r--r--SmartDeviceLink/SDLLockScreenPresenter.m6
-rw-r--r--SmartDeviceLink/SDLLogFileModuleMap.m26
-rw-r--r--SmartDeviceLink/SDLManagerDelegate.h10
-rw-r--r--SmartDeviceLink/SDLProtocol.m4
-rw-r--r--SmartDeviceLink/SDLRPCStruct.h4
-rw-r--r--SmartDeviceLink/SDLRPCStruct.m3
-rw-r--r--SmartDeviceLink/SDLSecondaryTransportManager.m350
-rw-r--r--SmartDeviceLink/SDLTCPTransport.m12
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleManagerSpec.m32
-rw-r--r--SmartDeviceLinkTests/ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m6
-rw-r--r--SmartDeviceLinkTests/ProxySpecs/SDLSecondaryTransportManagerSpec.m53
-rw-r--r--SmartDeviceLinkTests/RPCSpecs/SuperclassSpecs/SDLRPCStructSpec.m3
16 files changed, 365 insertions, 226 deletions
diff --git a/Example Apps/Example ObjC/MenuManager.m b/Example Apps/Example ObjC/MenuManager.m
index e81f0c2e3..8e68d91b6 100644
--- a/Example Apps/Example ObjC/MenuManager.m
+++ b/Example Apps/Example ObjC/MenuManager.m
@@ -137,7 +137,13 @@ NS_ASSUME_NONNULL_BEGIN
SDLSlider *sliderRPC = [[SDLSlider alloc] initWithNumTicks:3 position:1 sliderHeader:@"Select a letter" sliderFooters:@[@"A", @"B", @"C"] timeout:10000];
[manager sendRequest:sliderRPC withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) {
if(![response.resultCode isEqualToEnum:SDLResultSuccess]) {
- [manager sendRequest:[AlertManager alertWithMessageAndCloseButton:@"Slider could not be displayed" textField2:nil iconName:nil]];
+ if ([response.resultCode isEqualToEnum:SDLResultTimedOut]) {
+ [manager sendRequest:[AlertManager alertWithMessageAndCloseButton:@"Slider timed out" textField2:nil iconName:nil]];
+ } else if ([response.resultCode isEqualToEnum:SDLResultAborted]) {
+ [manager sendRequest:[AlertManager alertWithMessageAndCloseButton:@"Slider cancelled" textField2:nil iconName:nil]];
+ } else {
+ [manager sendRequest:[AlertManager alertWithMessageAndCloseButton:@"Slider could not be displayed" textField2:nil iconName:nil]];
+ }
}
}];
}];
@@ -148,8 +154,14 @@ NS_ASSUME_NONNULL_BEGIN
SDLScrollableMessage *messageRPC = [[SDLScrollableMessage alloc] initWithMessage:@"This is a scrollable message\nIt can contain many lines"];
[manager sendRequest:messageRPC withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) {
if(![response.resultCode isEqualToEnum:SDLResultSuccess]) {
- [manager sendRequest:[AlertManager alertWithMessageAndCloseButton:@"Scrollable Message could not be displayed" textField2:nil iconName:nil]];
- }
+ if ([response.resultCode isEqualToEnum:SDLResultTimedOut]) {
+ [manager sendRequest:[AlertManager alertWithMessageAndCloseButton:@"Scrollable Message timed out" textField2:nil iconName:nil]];
+ } else if ([response.resultCode isEqualToEnum:SDLResultAborted]) {
+ [manager sendRequest:[AlertManager alertWithMessageAndCloseButton:@"Scrollable Message cancelled" textField2:nil iconName:nil]];
+ } else {
+ [manager sendRequest:[AlertManager alertWithMessageAndCloseButton:@"Scrollable Message could not be displayed" textField2:nil iconName:nil]];
+ }
+ }
}];
}];
}
diff --git a/Example Apps/Example Swift/MenuManager.swift b/Example Apps/Example Swift/MenuManager.swift
index e014fa44b..2f1b92ec5 100644
--- a/Example Apps/Example Swift/MenuManager.swift
+++ b/Example Apps/Example Swift/MenuManager.swift
@@ -181,8 +181,15 @@ private extension MenuManager {
return SDLMenuCell(title: ACSliderMenuName, icon: nil, voiceCommands: [ACSliderMenuName], handler: { _ in
let slider = SDLSlider(numTicks: 3, position: 1, sliderHeader: "Select a letter", sliderFooters: ["A", "B", "C"], timeout: 3000)
manager.send(request: slider, responseHandler: { (request, response, error) in
- guard let response = response, response.resultCode == .success else {
- manager.send(AlertManager.alertWithMessageAndCloseButton("Slider could not be displayed"))
+ guard let response = response else { return }
+ guard response.resultCode == .success else {
+ if response.resultCode == .timedOut {
+ manager.send(AlertManager.alertWithMessageAndCloseButton("Slider timed out"))
+ } else if response.resultCode == .aborted {
+ manager.send(AlertManager.alertWithMessageAndCloseButton("Slider cancelled"))
+ } else {
+ manager.send(AlertManager.alertWithMessageAndCloseButton("Slider could not be displayed"))
+ }
return
}
})
@@ -193,8 +200,15 @@ private extension MenuManager {
return SDLMenuCell(title: ACScrollableMessageMenuName, icon: nil, voiceCommands: [ACScrollableMessageMenuName], handler: { _ in
let scrollableMessage = SDLScrollableMessage(message: "This is a scrollable message\nIt can contain many lines")
manager.send(request: scrollableMessage, responseHandler: { (request, response, error) in
- guard let response = response, response.resultCode == .success else {
- manager.send(AlertManager.alertWithMessageAndCloseButton("Scrollable could not be displayed"))
+ guard let response = response else { return }
+ guard response.resultCode == .success else {
+ if response.resultCode == .timedOut {
+ manager.send(AlertManager.alertWithMessageAndCloseButton("Scrollable Message timed out"))
+ } else if response.resultCode == .aborted {
+ manager.send(AlertManager.alertWithMessageAndCloseButton("Scrollable Message cancelled"))
+ } else {
+ manager.send(AlertManager.alertWithMessageAndCloseButton("Scrollable Message could not be displayed"))
+ }
return
}
})
diff --git a/SmartDeviceLink/SDLLifecycleManager.h b/SmartDeviceLink/SDLLifecycleManager.h
index 519cdbf8f..e3fcd2cb2 100644
--- a/SmartDeviceLink/SDLLifecycleManager.h
+++ b/SmartDeviceLink/SDLLifecycleManager.h
@@ -13,6 +13,7 @@
#import "SDLHMILevel.h"
#import "SDLLanguage.h"
#import "SDLSystemContext.h"
+#import "SDLVideoStreamingState.h"
@class SDLConfiguration;
@class SDLFileManager;
@@ -85,6 +86,7 @@ typedef void (^SDLManagerReadyBlock)(BOOL success, NSError *_Nullable error);
@property (copy, nonatomic, readonly) SDLLifecycleState *lifecycleState;
@property (copy, nonatomic, nullable) SDLHMILevel hmiLevel;
@property (copy, nonatomic, nullable) SDLAudioStreamingState audioStreamingState;
+@property (copy, nonatomic, nullable) SDLVideoStreamingState videoStreamingState;
@property (copy, nonatomic, nullable) SDLSystemContext systemContext;
@property (strong, nonatomic, nullable) SDLRegisterAppInterfaceResponse *registerResponse;
diff --git a/SmartDeviceLink/SDLLifecycleManager.m b/SmartDeviceLink/SDLLifecycleManager.m
index 53f2059ac..05a09dd9b 100644
--- a/SmartDeviceLink/SDLLifecycleManager.m
+++ b/SmartDeviceLink/SDLLifecycleManager.m
@@ -244,8 +244,14 @@ NSString *const BackgroundTaskTransportName = @"com.sdl.transport.backgroundTask
} else if (self.configuration.lifecycleConfig.allowedSecondaryTransports == SDLSecondaryTransportsNone) {
self.proxy = [SDLProxy iapProxyWithListener:self.notificationDispatcher secondaryTransportManager:nil encryptionLifecycleManager:self.encryptionLifecycleManager];
} else {
- // We reuse our queue to run secondary transport manager's state machine
- self.secondaryTransportManager = [[SDLSecondaryTransportManager alloc] initWithStreamingProtocolDelegate:self serialQueue:self.lifecycleQueue];
+ if([self.configuration.lifecycleConfig.appType isEqualToEnum:SDLAppHMITypeNavigation] ||
+ [self.configuration.lifecycleConfig.appType isEqualToEnum:SDLAppHMITypeProjection] ||
+ [self.configuration.lifecycleConfig.additionalAppTypes containsObject:SDLAppHMITypeNavigation] ||
+ [self.configuration.lifecycleConfig.additionalAppTypes containsObject:SDLAppHMITypeProjection]) {
+ // We reuse our queue to run secondary transport manager's state machine
+ self.secondaryTransportManager = [[SDLSecondaryTransportManager alloc] initWithStreamingProtocolDelegate:self serialQueue:self.lifecycleQueue];
+ }
+
self.proxy = [SDLProxy iapProxyWithListener:self.notificationDispatcher secondaryTransportManager:self.secondaryTransportManager encryptionLifecycleManager:self.encryptionLifecycleManager];
}
#pragma clang diagnostic pop
@@ -284,6 +290,7 @@ NSString *const BackgroundTaskTransportName = @"com.sdl.transport.backgroundTask
self.lastCorrelationId = 0;
self.hmiLevel = nil;
self.audioStreamingState = nil;
+ self.videoStreamingState = nil;
self.systemContext = nil;
// Due to a race condition internally with EAStream, we cannot immediately attempt to restart the proxy, as we will randomly crash.
@@ -529,6 +536,10 @@ NSString *const BackgroundTaskTransportName = @"com.sdl.transport.backgroundTask
[self.delegate audioStreamingState:SDLAudioStreamingStateNotAudible didChangeToState:self.audioStreamingState];
}
+ if ([self.delegate respondsToSelector:@selector(videoStreamingState:didChangetoState:)]) {
+ [self.delegate videoStreamingState:SDLVideoStreamingStateNotStreamable didChangetoState:self.videoStreamingState];
+ }
+
// Stop the background task now that setup has completed
[self.backgroundTaskManager endBackgroundTask];
}
@@ -795,9 +806,12 @@ NSString *const BackgroundTaskTransportName = @"com.sdl.transport.backgroundTask
SDLHMILevel oldHMILevel = self.hmiLevel;
self.hmiLevel = hmiStatusNotification.hmiLevel;
- SDLAudioStreamingState oldStreamingState = self.audioStreamingState;
+ SDLAudioStreamingState oldAudioStreamingState = self.audioStreamingState;
self.audioStreamingState = hmiStatusNotification.audioStreamingState;
+ SDLVideoStreamingState oldVideoStreamingState = self.videoStreamingState;
+ self.videoStreamingState = hmiStatusNotification.videoStreamingState;
+
SDLSystemContext oldSystemContext = self.systemContext;
self.systemContext = hmiStatusNotification.systemContext;
@@ -805,8 +819,12 @@ NSString *const BackgroundTaskTransportName = @"com.sdl.transport.backgroundTask
SDLLogD(@"HMI level changed from %@ to %@", oldHMILevel, self.hmiLevel);
}
- if (![oldStreamingState isEqualToEnum:self.audioStreamingState]) {
- SDLLogD(@"Audio streaming state changed from %@ to %@", oldStreamingState, self.audioStreamingState);
+ if (![oldAudioStreamingState isEqualToEnum:self.audioStreamingState]) {
+ SDLLogD(@"Audio streaming state changed from %@ to %@", oldAudioStreamingState, self.audioStreamingState);
+ }
+
+ if (![oldVideoStreamingState isEqualToEnum:self.videoStreamingState]) {
+ SDLLogD(@"Video streaming state changed from %@ to %@", oldVideoStreamingState, self.videoStreamingState);
}
if (![oldSystemContext isEqualToEnum:self.systemContext]) {
@@ -826,10 +844,16 @@ NSString *const BackgroundTaskTransportName = @"com.sdl.transport.backgroundTask
[self.delegate hmiLevel:oldHMILevel didChangeToLevel:self.hmiLevel];
}
- if (![oldStreamingState isEqualToEnum:self.audioStreamingState]
- && !(oldStreamingState == nil && self.audioStreamingState == nil)
+ if (![oldAudioStreamingState isEqualToEnum:self.audioStreamingState]
+ && !(oldAudioStreamingState == nil && self.audioStreamingState == nil)
&& [self.delegate respondsToSelector:@selector(audioStreamingState:didChangeToState:)]) {
- [self.delegate audioStreamingState:oldStreamingState didChangeToState:self.audioStreamingState];
+ [self.delegate audioStreamingState:oldAudioStreamingState didChangeToState:self.audioStreamingState];
+ }
+
+ if (![oldVideoStreamingState isEqualToEnum:self.videoStreamingState]
+ && !(oldVideoStreamingState == nil && self.videoStreamingState == nil)
+ && [self.delegate respondsToSelector:@selector(videoStreamingState:didChangetoState:)]) {
+ [self.delegate videoStreamingState:oldVideoStreamingState didChangetoState:self.videoStreamingState];
}
if (![oldSystemContext isEqualToEnum:self.systemContext]
diff --git a/SmartDeviceLink/SDLLockScreenPresenter.m b/SmartDeviceLink/SDLLockScreenPresenter.m
index d84b3bf95..80bc703b7 100644
--- a/SmartDeviceLink/SDLLockScreenPresenter.m
+++ b/SmartDeviceLink/SDLLockScreenPresenter.m
@@ -163,6 +163,12 @@ NS_ASSUME_NONNULL_BEGIN
if (completionHandler == nil) { return; }
return completionHandler();
}
+
+ if (self.lockViewController.presentingViewController == nil) {
+ SDLLogW(@"Attempted to dismiss lockscreen, but lockViewController is not presented");
+ if (completionHandler == nil) { return; }
+ return completionHandler();
+ }
// Let ourselves know that the lockscreen will dismiss so we can pause video streaming for a few milliseconds - otherwise the animation to dismiss the lockscreen will be very janky.
[[NSNotificationCenter defaultCenter] postNotificationName:SDLLockScreenManagerWillDismissLockScreenViewController object:nil];
diff --git a/SmartDeviceLink/SDLLogFileModuleMap.m b/SmartDeviceLink/SDLLogFileModuleMap.m
index 4e8ed5808..9195840e0 100644
--- a/SmartDeviceLink/SDLLogFileModuleMap.m
+++ b/SmartDeviceLink/SDLLogFileModuleMap.m
@@ -14,6 +14,9 @@
+ (NSSet<SDLLogFileModule *> *)sdlModuleMap {
return [NSSet setWithArray:@[[self sdl_transportModule],
+ [self sdl_tcpTransportModule],
+ [self sdl_iapTransportModule],
+ [self sdl_secondaryTransportModule],
[self sdl_proxyModule],
[self sdl_protocolModule],
[self sdl_rpcModule],
@@ -33,10 +36,26 @@
[self sdl_utilitiesModule]]];
}
+#pragma mark Transport
+
+ (SDLLogFileModule *)sdl_transportModule {
- return [SDLLogFileModule moduleWithName:@"Transport" files:[NSSet setWithArray:@[@"SDLIAPSession", @"SDLIAPTransport", @"SDLIAPDataSession", @"SDLIAPControlSession", @"SDLSecondaryTransportManager", @"SDLSecondaryTransportPrimaryProtocolHandler", @"SDLStreamDelegate", @"SDLTCPTransport"]]];
+ return [SDLLogFileModule moduleWithName:@"Transport" files:[NSSet setWithArray:@[@"SDLStreamDelegate"]]];
+}
+
++ (SDLLogFileModule *)sdl_tcpTransportModule {
+ return [SDLLogFileModule moduleWithName:@"Transport/TCP" files:[NSSet setWithArray:@[@"SDLTCPTransport"]]];
+}
+
++ (SDLLogFileModule *)sdl_iapTransportModule {
+ return [SDLLogFileModule moduleWithName:@"Transport/IAP" files:[NSSet setWithArray:@[@"SDLIAPSession", @"SDLIAPTransport", @"SDLIAPDataSession", @"SDLIAPControlSession"]]];
+}
+
++ (SDLLogFileModule *)sdl_secondaryTransportModule {
+ return [SDLLogFileModule moduleWithName:@"Transport/Secondary" files:[NSSet setWithArray:@[@"SDLSecondaryTransportManager", @"SDLSecondaryTransportPrimaryProtocolHandler"]]];
}
+#pragma mark Low-Level
+
+ (SDLLogFileModule *)sdl_proxyModule {
return [SDLLogFileModule moduleWithName:@"Proxy" files:[NSSet setWithArray:@[@"SDLProxy", @"SDLPolicyDataParser"]]];
}
@@ -49,13 +68,12 @@
return [SDLLogFileModule moduleWithName:@"RPC" files:[NSSet setWithArray:@[@"SDLRPCPayload", @"NSMutableDictionary+Store"]]];
}
+#pragma mark Managers
+
+ (SDLLogFileModule *)sdl_dispatcherModule {
return [SDLLogFileModule moduleWithName:@"Dispatcher" files:[NSSet setWithArray:@[@"SDLNotificationDispatcher", @"SDLResponseDispatcher"]]];
}
-
-#pragma mark Managers
-
+ (SDLLogFileModule *)sdl_fileManagerModule {
return [SDLLogFileModule moduleWithName:@"File" files:[NSSet setWithArray:@[@"SDLFileManager", @"SDLFile", @"SDLArtwork", @"SDLListFilesOperation", @"SDLUploadFileOperation", @"SDLDeleteFileOperation"]]];
}
diff --git a/SmartDeviceLink/SDLManagerDelegate.h b/SmartDeviceLink/SDLManagerDelegate.h
index 538b11023..9cf795e37 100644
--- a/SmartDeviceLink/SDLManagerDelegate.h
+++ b/SmartDeviceLink/SDLManagerDelegate.h
@@ -13,6 +13,7 @@
#import "SDLSystemContext.h"
#import "SDLLifecycleConfigurationUpdate.h"
#import "SDLLanguage.h"
+#import "SDLVideoStreamingState.h"
NS_ASSUME_NONNULL_BEGIN
@@ -41,6 +42,14 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)audioStreamingState:(nullable SDLAudioStreamingState)oldState didChangeToState:(SDLAudioStreamingState)newState;
+/// Called when the video streaming state of this application changes. This refers to streaming video for navigation purposes. If you are "autostreaming" via CarWindow, you should not do anything with this method. Everything should be handled for you automatically.
+///
+/// Only supported on RPC v5.0+ connections.
+///
+/// @param oldState The previous state
+/// @param newState The current state
+- (void)videoStreamingState:(nullable SDLVideoStreamingState)oldState didChangetoState:(SDLVideoStreamingState)newState;
+
/**
* Called when the system context of this application changes on the remote system. This refers to whether or not a user-initiated interaction is in progress, and if so, what it is.
*
@@ -57,7 +66,6 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (nullable SDLLifecycleConfigurationUpdate *)managerShouldUpdateLifecycleToLanguage:(SDLLanguage)language;
-
@end
NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLProtocol.m b/SmartDeviceLink/SDLProtocol.m
index f71da8354..29c7ca38f 100644
--- a/SmartDeviceLink/SDLProtocol.m
+++ b/SmartDeviceLink/SDLProtocol.m
@@ -286,11 +286,7 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)sendRPC:(SDLRPCMessage *)message encrypted:(BOOL)encryption error:(NSError *__autoreleasing *)error {
NSParameterAssert(message != nil);
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:[message serializeAsDictionary:(Byte)[SDLGlobals sharedGlobals].protocolVersion.major] options:kNilOptions error:error];
-#pragma clang diagnostic pop
if (error != nil) {
SDLLogW(@"Error encoding JSON data: %@", *error);
diff --git a/SmartDeviceLink/SDLRPCStruct.h b/SmartDeviceLink/SDLRPCStruct.h
index 523400261..e8a36d903 100644
--- a/SmartDeviceLink/SDLRPCStruct.h
+++ b/SmartDeviceLink/SDLRPCStruct.h
@@ -23,7 +23,7 @@ NS_ASSUME_NONNULL_BEGIN
* @param dict A dictionary
* @return A SDLRPCStruct object
*/
-- (instancetype)initWithDictionary:(NSDictionary<NSString *, id> *)dict __deprecated_msg("This is not intended for public use");
+- (instancetype)initWithDictionary:(NSDictionary<NSString *, id> *)dict;
/**
* Converts struct to JSON formatted data
@@ -31,7 +31,7 @@ NS_ASSUME_NONNULL_BEGIN
* @param version The protocol version
* @return JSON formatted data
*/
-- (NSDictionary<NSString *, id> *)serializeAsDictionary:(Byte)version __deprecated_msg("This is not intended for public use");
+- (NSDictionary<NSString *, id> *)serializeAsDictionary:(Byte)version;
@end
diff --git a/SmartDeviceLink/SDLRPCStruct.m b/SmartDeviceLink/SDLRPCStruct.m
index e3de83f73..9956df03d 100644
--- a/SmartDeviceLink/SDLRPCStruct.m
+++ b/SmartDeviceLink/SDLRPCStruct.m
@@ -54,8 +54,6 @@ NS_ASSUME_NONNULL_BEGIN
return [self.store description];
}
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ (NSDictionary<NSString *, id> *)sdl_serializeDictionary:(NSDictionary *)dict version:(Byte)version {
NSMutableDictionary<NSString *, id> *ret = [NSMutableDictionary dictionaryWithCapacity:dict.count];
for (NSString *key in dict.keyEnumerator) {
@@ -82,7 +80,6 @@ NS_ASSUME_NONNULL_BEGIN
}
return ret;
}
-#pragma clang diagnostic pop
- (id)copyWithZone:(nullable NSZone *)zone {
SDLRPCStruct *newStruct = [[[self class] allocWithZone:zone] initWithDictionary:_store];
diff --git a/SmartDeviceLink/SDLSecondaryTransportManager.m b/SmartDeviceLink/SDLSecondaryTransportManager.m
index 860abcbaf..9cd1af232 100644
--- a/SmartDeviceLink/SDLSecondaryTransportManager.m
+++ b/SmartDeviceLink/SDLSecondaryTransportManager.m
@@ -16,9 +16,11 @@
#import "SDLControlFramePayloadTransportEventUpdate.h"
#import "SDLIAPTransport.h"
#import "SDLLogMacros.h"
+#import "SDLOnHMIStatus.h"
#import "SDLProtocol.h"
#import "SDLProtocolHeader.h"
#import "SDLNotificationConstants.h"
+#import "SDLRPCNotificationNotification.h"
#import "SDLSecondaryTransportPrimaryProtocolHandler.h"
#import "SDLStateMachine.h"
#import "SDLTCPTransport.h"
@@ -75,9 +77,9 @@ static const int TCPPortUnspecified = -1;
// Selected type of secondary transport. If 'SDLSecondaryTransportTypeDisabled' then secondary transport is disabled.
@property (assign, nonatomic) SDLSecondaryTransportType secondaryTransportType;
// Instance of the transport for secondary transport.
-@property (nullable, strong, nonatomic) id<SDLTransportType> secondaryTransport;
+@property (strong, nonatomic, nullable) id<SDLTransportType> secondaryTransport;
// Instance of the protocol that runs on secondary transport.
-@property (nullable, strong, nonatomic) SDLProtocol *secondaryProtocol;
+@property (strong, nonatomic, nullable) SDLProtocol *secondaryProtocol;
// Timer to check Register Secondary Transport ACK response on secondary transport.
@property (strong, nonatomic, nullable) SDLTimer *registerTransportTimer;
@@ -85,9 +87,9 @@ static const int TCPPortUnspecified = -1;
@property (weak, nonatomic) id<SDLStreamingProtocolDelegate> streamingProtocolDelegate;
// Configuration sent by system; list of transports that are allowed to carry audio service
-@property (strong, nonatomic, nonnull) NSArray<SDLTransportClassBox *> *transportsForAudioService;
+@property (strong, nonatomic) NSArray<SDLTransportClassBox *> *transportsForAudioService;
// Configuration sent by system; list of transports that are allowed to carry video service
-@property (strong, nonatomic, nonnull) NSArray<SDLTransportClassBox *> *transportsForVideoService;
+@property (strong, nonatomic) NSArray<SDLTransportClassBox *> *transportsForVideoService;
// A map to remember which service is currently running on which transport
@property (strong, nonatomic) NSMutableDictionary<SDLServiceTypeBox *, SDLTransportClassBox *> *streamingServiceTransportMap;
@@ -95,8 +97,8 @@ static const int TCPPortUnspecified = -1;
@property (strong, nonatomic, nullable) NSString *ipAddress;
// TCP port number of SDL Core. If the information isn't available then TCPPortUnspecified is stored.
@property (assign, nonatomic) int tcpPort;
-// App is ready to set security manager to secondary protocol
-@property (assign, nonatomic, getter=isAppReady) BOOL appReady;
+
+@property (strong, nonatomic, nullable) SDLHMILevel currentHMILevel;
@end
@@ -122,7 +124,7 @@ static const int TCPPortUnspecified = -1;
@(SDLServiceTypeVideo):@(SDLTransportClassInvalid)} mutableCopy];
_tcpPort = TCPPortUnspecified;
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidBecomeReady) name:SDLDidBecomeReady object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_hmiStatusDidChange:) name:SDLDidChangeHMIStatusNotification object:nil];
return self;
}
@@ -161,50 +163,76 @@ static const int TCPPortUnspecified = -1;
[self.stateMachine transitionToState:SDLSecondaryTransportStateStopped];
}
-// called from SDLProtocol's _receiveQueue of "primary" transport
-- (void)onStartServiceAckReceived:(SDLControlFramePayloadRPCStartServiceAck *)payload {
- NSMutableArray<SDLSecondaryTransportTypeBox *> *secondaryTransports = nil;
- if (payload.secondaryTransports != nil) {
- secondaryTransports = [NSMutableArray array];
- for (NSString *transportString in payload.secondaryTransports) {
- SDLSecondaryTransportType transport = [self sdl_convertTransportType:transportString];
- [secondaryTransports addObject:@(transport)];
- }
- }
- NSArray<SDLTransportClassBox *> *transportsForAudio = [self sdl_convertServiceTransports:payload.audioServiceTransports];
- NSArray<SDLTransportClassBox *> *transportsForVideo = [self sdl_convertServiceTransports:payload.videoServiceTransports];
+#pragma mark - Manager Lifecycle
- SDLLogV(@"Secondary transports: %@, transports for audio: %@, transports for video: %@", secondaryTransports, transportsForAudio, transportsForVideo);
+- (void)sdl_startManager {
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_onAppStateUpdated:) name:UIApplicationDidBecomeActiveNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_onAppStateUpdated:) name:UIApplicationWillResignActiveNotification object:nil];
- dispatch_async(_stateMachineQueue, ^{
- [self sdl_configureManager:secondaryTransports availableTransportsForAudio:transportsForAudio availableTransportsForVideo:transportsForVideo];
- });
+ [self.primaryProtocolHandler start];
}
-// called from SDLProtocol's _receiveQueue of "primary" transport
-- (void)onTransportEventUpdateReceived:(SDLControlFramePayloadTransportEventUpdate *)payload {
- dispatch_async(_stateMachineQueue, ^{
- BOOL updated = NO;
+- (void)sdl_stopManager {
+ SDLLogD(@"SDLSecondaryTransportManager stop");
- if (payload.tcpIpAddress != nil) {
- if (![self.ipAddress isEqualToString:payload.tcpIpAddress]) {
- self.ipAddress = payload.tcpIpAddress;
- updated = YES;
- SDLLogD(@"TCP transport IP address updated: %@", self.ipAddress);
- }
- }
- if (payload.tcpPort != SDLControlFrameInt32NotFound) {
- if (self.tcpPort != payload.tcpPort) {
- self.tcpPort = payload.tcpPort;
- updated = YES;
- SDLLogD(@"TCP transport port number updated: %d", self.tcpPort);
- }
- }
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
- if (updated) {
- [self sdl_handleTransportEventUpdate];
- }
- });
+ [self.primaryProtocolHandler stop];
+
+ self.streamingServiceTransportMap = [@{@(SDLServiceTypeAudio):@(SDLTransportClassInvalid),
+ @(SDLServiceTypeVideo):@(SDLTransportClassInvalid)} mutableCopy];
+ self.secondaryTransportType = SDLSecondaryTransportTypeDisabled;
+ self.transportsForAudioService = @[];
+ self.transportsForVideoService = @[];
+
+ self.ipAddress = nil;
+ self.tcpPort = TCPPortUnspecified;
+ self.currentHMILevel = nil;
+}
+
+- (void)sdl_configureManager:(nullable NSArray<SDLSecondaryTransportTypeBox *> *)availableSecondaryTransports
+ availableTransportsForAudio:(nullable NSArray<SDLTransportClassBox *> *)availableTransportsForAudio
+ availableTransportsForVideo:(nullable NSArray<SDLTransportClassBox *> *)availableTransportsForVideo {
+ if (![self.stateMachine isCurrentState:SDLSecondaryTransportStateStarted]) {
+ SDLLogW(@"SecondaryTransportManager ignores duplicate Start Service ACK frame");
+ return;
+ }
+
+ // default values
+ self.secondaryTransportType = SDLSecondaryTransportTypeDisabled;
+ self.transportsForAudioService = @[@(SDLTransportClassPrimary)]; // If SDL Core did not send a transport list for the service, start it on Primary Transport.
+ self.transportsForVideoService = @[@(SDLTransportClassPrimary)];
+ BOOL validConfig = YES;
+
+ if (availableSecondaryTransports.count > 0) {
+ // current proposal says the list should contain only one element
+ SDLSecondaryTransportTypeBox *transportType = availableSecondaryTransports[0];
+ self.secondaryTransportType = [transportType integerValue];
+ } else {
+ SDLLogW(@"Did not receive secondary transport type from system. Secondary transport is disabled.");
+ }
+
+ SDLSecondaryTransportType primaryTransportType = [self sdl_getTransportTypeFromProtocol:self.primaryProtocol];
+ if (self.secondaryTransportType == primaryTransportType) {
+ SDLLogW(@"Same transport is specified for both primary and secondary transport. Secondary transport is disabled.");
+ self.secondaryTransportType = SDLSecondaryTransportTypeDisabled;
+ validConfig = NO; // let audio and video services start on primary transport
+ } else if (self.secondaryTransportType == SDLSecondaryTransportTypeIAP) {
+ SDLLogW(@"Starting IAP as secondary transport, which does not usually happen");
+ }
+
+ if (availableTransportsForAudio != nil && validConfig) {
+ self.transportsForAudioService = availableTransportsForAudio;
+ }
+ if (availableTransportsForVideo != nil && validConfig) {
+ self.transportsForVideoService = availableTransportsForVideo;
+ }
+
+ // this will trigger audio / video streaming if they are allowed on primary transport
+ [self sdl_handleTransportUpdateWithPrimaryAvailable:YES secondaryAvailable:NO];
+
+ [self.stateMachine transitionToState:SDLSecondaryTransportStateConfigured];
}
#pragma mark - State machine
@@ -221,21 +249,28 @@ static const int TCPPortUnspecified = -1;
}
- (void)didEnterStateStopped {
+ SDLLogD(@"Secondary transport manager stopped");
[self sdl_stopManager];
}
- (void)didEnterStateStarted {
+ SDLLogD(@"Secondary transport manager started");
[self sdl_startManager];
}
- (void)didEnterStateConfigured {
- if ((self.secondaryTransportType == SDLSecondaryTransportTypeTCP && [self sdl_isTCPReady] && self.isAppReady)
- || (self.secondaryTransportType == SDLSecondaryTransportTypeIAP && self.isAppReady)) {
+ SDLLogD(@"Secondary transport manager is configured");
+ // If this is a TCP transport, check if it's ready. If it's IAP, we can just continue. In both cases, check if HMI level is Non-NONE
+ // https://github.com/smartdevicelink/sdl_evolution/blob/master/proposals/0214-secondary-transport-optimization.md
+ if (((self.secondaryTransportType == SDLSecondaryTransportTypeTCP && self.sdl_isTCPReady)
+ || self.secondaryTransportType == SDLSecondaryTransportTypeIAP)
+ && self.sdl_isHMILevelNonNone) {
[self.stateMachine transitionToState:SDLSecondaryTransportStateConnecting];
}
}
- (void)didEnterStateConnecting {
+ SDLLogD(@"Secondary transport is connecting");
[self sdl_connectSecondaryTransport];
}
@@ -258,6 +293,7 @@ static const int TCPPortUnspecified = -1;
}
- (void)didEnterStateRegistered {
+ SDLLogD(@"Secondary transport is registered");
[self sdl_handleTransportUpdateWithPrimaryAvailable:YES secondaryAvailable:YES];
}
@@ -282,114 +318,16 @@ static const int TCPPortUnspecified = -1;
}
- (void)didEnterStateReconnecting {
- self.appReady = NO;
+ SDLLogD(@"Secondary transport is reconnecting");
__weak typeof(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(RetryConnectionDelay * NSEC_PER_SEC)), _stateMachineQueue, ^{
if ([weakSelf.stateMachine isCurrentState:SDLSecondaryTransportStateReconnecting]) {
- SDLLogD(@"Retry secondary transport after disconnection or registration failure");
+ SDLLogD(@"Attempting reconnection");
[weakSelf.stateMachine transitionToState:SDLSecondaryTransportStateConfigured];
}
});
}
-- (void)sdl_startManager {
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_onAppStateUpdated:) name:UIApplicationDidBecomeActiveNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_onAppStateUpdated:) name:UIApplicationWillResignActiveNotification object:nil];
-
- [self.primaryProtocolHandler start];
-}
-
-- (void)sdl_stopManager {
- SDLLogD(@"SDLSecondaryTransportManager stop");
-
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
-
- [self.primaryProtocolHandler stop];
-
- self.streamingServiceTransportMap = [@{@(SDLServiceTypeAudio):@(SDLTransportClassInvalid),
- @(SDLServiceTypeVideo):@(SDLTransportClassInvalid)} mutableCopy];
- self.secondaryTransportType = SDLSecondaryTransportTypeDisabled;
- self.transportsForAudioService = @[];
- self.transportsForVideoService = @[];
-
- self.ipAddress = nil;
- self.tcpPort = TCPPortUnspecified;
-}
-
-- (void)sdl_configureManager:(nullable NSArray<SDLSecondaryTransportTypeBox *> *)availableSecondaryTransports
- availableTransportsForAudio:(nullable NSArray<SDLTransportClassBox *> *)availableTransportsForAudio
- availableTransportsForVideo:(nullable NSArray<SDLTransportClassBox *> *)availableTransportsForVideo {
- if (![self.stateMachine isCurrentState:SDLSecondaryTransportStateStarted]) {
- SDLLogW(@"SecondaryTransportManager ignores duplicate Start Service ACK frame");
- return;
- }
-
- // default values
- self.secondaryTransportType = SDLSecondaryTransportTypeDisabled;
- self.transportsForAudioService = @[@(SDLTransportClassPrimary)]; // If SDL Core did not send a transport list for the service, start it on Primary Transport.
- self.transportsForVideoService = @[@(SDLTransportClassPrimary)];
- BOOL validConfig = YES;
-
- if (availableSecondaryTransports.count > 0) {
- // current proposal says the list should contain only one element
- SDLSecondaryTransportTypeBox *transportType = availableSecondaryTransports[0];
- self.secondaryTransportType = [transportType integerValue];
- } else {
- SDLLogW(@"Did not receive secondary transport type from system. Secondary transport is disabled.");
- }
-
- SDLSecondaryTransportType primaryTransportType = [self sdl_getTransportTypeFromProtocol:self.primaryProtocol];
- if (self.secondaryTransportType == primaryTransportType) {
- SDLLogW(@"Same transport is specified for both primary and secondary transport. Secondary transport is disabled.");
- self.secondaryTransportType = SDLSecondaryTransportTypeDisabled;
- validConfig = NO; // let audio and video services start on primary transport
- } else if (self.secondaryTransportType == SDLSecondaryTransportTypeIAP) {
- SDLLogW(@"Starting IAP as secondary transport, which does not usually happen");
- }
-
- if (availableTransportsForAudio != nil && validConfig) {
- self.transportsForAudioService = availableTransportsForAudio;
- }
- if (availableTransportsForVideo != nil && validConfig) {
- self.transportsForVideoService = availableTransportsForVideo;
- }
-
- // this will trigger audio / video streaming if they are allowed on primary transport
- [self sdl_handleTransportUpdateWithPrimaryAvailable:YES secondaryAvailable:NO];
-
- [self.stateMachine transitionToState:SDLSecondaryTransportStateConfigured];
-}
-
-- (void)sdl_handleTransportEventUpdate {
- if ([self.stateMachine isCurrentState:SDLSecondaryTransportStateStarted]) {
- // The system sent Transport Event Update frame prior to Start Service ACK. Just keep the information and do nothing here.
- SDLLogV(@"Received TCP transport information prior to Start Service ACK");
- return;
- }
- if (self.secondaryTransportType != SDLSecondaryTransportTypeTCP) {
- SDLLogV(@"Received TCP transport information while the transport is not TCP");
- return;
- }
-
- if ([self.stateMachine isCurrentState:SDLSecondaryTransportStateConfigured] && [self sdl_isTCPReady] && self.isAppReady) {
- [self.stateMachine transitionToState:SDLSecondaryTransportStateConnecting];
- } else if ([self sdl_isTransportOpened]) {
- // Disconnect current transport. If the IP address is available then we will reconnect immediately.
- SDLLogD(@"TCP transport information updated, disconnecting current transport");
- [self.stateMachine transitionToState:SDLSecondaryTransportStateConfigured];
- } else if ([self.stateMachine isCurrentState:SDLSecondaryTransportStateReconnecting]) {
- SDLLogD(@"TCP transport information updated, aborting reconnection timer");
- [self.stateMachine transitionToState:SDLSecondaryTransportStateConfigured];
- }
-}
-
-- (BOOL)sdl_isTransportOpened {
- return [self.stateMachine isCurrentState:SDLSecondaryTransportStateConnecting]
- || [self.stateMachine isCurrentState:SDLSecondaryTransportStateRegistered];
-}
-
-
#pragma mark - Starting / Stopping / Restarting services
- (void)sdl_handleTransportUpdateWithPrimaryAvailable:(BOOL)primaryAvailable secondaryAvailable:(BOOL)secondaryAvailable {
@@ -400,7 +338,7 @@ static const int TCPPortUnspecified = -1;
secondaryAvailable:secondaryAvailable
transportUpdatedBlock:^(SDLProtocol * _Nullable oldProtocol, SDLProtocol * _Nullable newProtocol) {
[self.streamingProtocolDelegate audioServiceProtocolDidUpdateFromOldProtocol:oldProtocol toNewProtocol:newProtocol];
- }];
+ }];
// update video service
[self sdl_updateService:SDLServiceTypeVideo
@@ -409,7 +347,7 @@ static const int TCPPortUnspecified = -1;
secondaryAvailable:secondaryAvailable
transportUpdatedBlock:^(SDLProtocol * _Nullable oldProtocol, SDLProtocol * _Nullable newProtocol) {
[self.streamingProtocolDelegate videoServiceProtocolDidUpdateFromOldProtocol:oldProtocol toNewProtocol:newProtocol];
- }];
+ }];
}
- (void)sdl_updateService:(UInt8)service
@@ -565,8 +503,8 @@ static const int TCPPortUnspecified = -1;
return NO;
}
- if ([self sdl_getAppState] != UIApplicationStateActive) {
- SDLLogD(@"App state is not Active, abort starting TCP transport");
+ if (self.sdl_getAppState != UIApplicationStateActive) {
+ SDLLogD(@"App state is not Active, TCP transport is not ready");
return NO;
}
@@ -645,6 +583,76 @@ static const int TCPPortUnspecified = -1;
});
}
+// called from SDLProtocol's _receiveQueue of "primary" transport
+- (void)onStartServiceAckReceived:(SDLControlFramePayloadRPCStartServiceAck *)payload {
+ NSMutableArray<SDLSecondaryTransportTypeBox *> *secondaryTransports = nil;
+ if (payload.secondaryTransports != nil) {
+ secondaryTransports = [NSMutableArray array];
+ for (NSString *transportString in payload.secondaryTransports) {
+ SDLSecondaryTransportType transport = [self sdl_convertTransportType:transportString];
+ [secondaryTransports addObject:@(transport)];
+ }
+ }
+
+ NSArray<SDLTransportClassBox *> *transportsForAudio = [self sdl_convertServiceTransports:payload.audioServiceTransports];
+ NSArray<SDLTransportClassBox *> *transportsForVideo = [self sdl_convertServiceTransports:payload.videoServiceTransports];
+
+ SDLLogV(@"Secondary transports: %@, transports for audio: %@, transports for video: %@", secondaryTransports, transportsForAudio, transportsForVideo);
+
+ dispatch_async(_stateMachineQueue, ^{
+ [self sdl_configureManager:secondaryTransports availableTransportsForAudio:transportsForAudio availableTransportsForVideo:transportsForVideo];
+ });
+}
+
+// called from SDLProtocol's _receiveQueue of "primary" transport
+- (void)onTransportEventUpdateReceived:(SDLControlFramePayloadTransportEventUpdate *)payload {
+ dispatch_async(_stateMachineQueue, ^{
+ BOOL updated = NO;
+
+ if (payload.tcpIpAddress != nil) {
+ if (![self.ipAddress isEqualToString:payload.tcpIpAddress]) {
+ self.ipAddress = payload.tcpIpAddress;
+ updated = YES;
+ SDLLogD(@"TCP transport IP address updated: %@", self.ipAddress);
+ }
+ }
+ if (payload.tcpPort != SDLControlFrameInt32NotFound) {
+ if (self.tcpPort != payload.tcpPort) {
+ self.tcpPort = payload.tcpPort;
+ updated = YES;
+ SDLLogD(@"TCP transport port number updated: %d", self.tcpPort);
+ }
+ }
+
+ if (updated) {
+ [self sdl_handleTransportEventUpdate];
+ }
+ });
+}
+
+- (void)sdl_handleTransportEventUpdate {
+ if ([self.stateMachine isCurrentState:SDLSecondaryTransportStateStarted]) {
+ // The system sent Transport Event Update frame prior to Start Service ACK. Just keep the information and do nothing here.
+ SDLLogV(@"Received TCP transport information prior to Start Service ACK");
+ return;
+ }
+ if (self.secondaryTransportType != SDLSecondaryTransportTypeTCP) {
+ SDLLogV(@"Received TCP transport information while the transport is not TCP");
+ return;
+ }
+
+ if ([self.stateMachine isCurrentState:SDLSecondaryTransportStateConfigured] && self.sdl_isTCPReady && self.sdl_isHMILevelNonNone) {
+ [self.stateMachine transitionToState:SDLSecondaryTransportStateConnecting];
+ } else if ([self sdl_isTransportOpened]) {
+ // Disconnect current transport. If the IP address is available then we will reconnect immediately.
+ SDLLogD(@"TCP transport information updated, disconnecting current transport");
+ [self.stateMachine transitionToState:SDLSecondaryTransportStateConfigured];
+ } else if ([self.stateMachine isCurrentState:SDLSecondaryTransportStateReconnecting]) {
+ SDLLogD(@"TCP transport information updated, aborting reconnection timer");
+ [self.stateMachine transitionToState:SDLSecondaryTransportStateConfigured];
+ }
+}
+
#pragma mark - App state handling
- (void)sdl_onAppStateUpdated:(NSNotification *)notification {
@@ -655,9 +663,11 @@ static const int TCPPortUnspecified = -1;
[self.stateMachine transitionToState:SDLSecondaryTransportStateConfigured];
}
} else if (notification.name == UIApplicationDidBecomeActiveNotification) {
- if (([self.stateMachine isCurrentState:SDLSecondaryTransportStateConfigured])
- && self.secondaryTransportType == SDLSecondaryTransportTypeTCP && [self sdl_isTCPReady] && self.isAppReady) {
- SDLLogD(@"Resuming TCP transport since the app becomes foreground");
+ if ([self.stateMachine isCurrentState:SDLSecondaryTransportStateConfigured]
+ && self.secondaryTransportType == SDLSecondaryTransportTypeTCP
+ && [self sdl_isTCPReady]
+ && [self sdl_isHMILevelNonNone]) {
+ SDLLogD(@"Resuming TCP transport since the app came into the foreground");
[self.stateMachine transitionToState:SDLSecondaryTransportStateConnecting];
}
}
@@ -713,11 +723,33 @@ static const int TCPPortUnspecified = -1;
}
}
-- (void)appDidBecomeReady {
- self.appReady = YES;
- if (([self.stateMachine.currentState isEqualToString:SDLSecondaryTransportStateConfigured] && self.tcpPort != SDLControlFrameInt32NotFound && self.ipAddress != nil)
- || self.secondaryTransportType == SDLSecondaryTransportTypeIAP) {
- [self.stateMachine transitionToState:SDLSecondaryTransportStateConnecting];
+- (BOOL)sdl_isTransportOpened {
+ return [self.stateMachine isCurrentState:SDLSecondaryTransportStateConnecting] || [self.stateMachine isCurrentState:SDLSecondaryTransportStateRegistered];
+}
+
+- (BOOL)sdl_isHMILevelNonNone {
+ return (self.currentHMILevel != nil && ![self.currentHMILevel isEqualToEnum:SDLHMILevelNone]);
+}
+
+#pragma mark - RPC Notifications
+/// Check and track the HMI status to ensure that the secondary transport only attempts a connection in non-NONE HMI states
+///
+/// See: https://github.com/smartdevicelink/sdl_evolution/blob/master/proposals/0214-secondary-transport-optimization.md
+///
+/// @param notification The NSNotification containing the OnHMIStatus
+- (void)sdl_hmiStatusDidChange:(SDLRPCNotificationNotification *)notification {
+ if (![notification isNotificationMemberOfClass:[SDLOnHMIStatus class]]) {
+ return;
+ }
+
+ SDLOnHMIStatus *hmiStatus = notification.notification;
+ self.currentHMILevel = hmiStatus.hmiLevel;
+
+ // If the HMI level is non-NONE, and the state machine is currently waiting in the configured state, and _either_ we're using TCP and it's ready _or_ we're using IAP.
+ if (self.sdl_isHMILevelNonNone
+ && [self.stateMachine isCurrentState:SDLSecondaryTransportStateConfigured]
+ && ((self.secondaryTransportType == SDLSecondaryTransportTypeTCP && [self sdl_isTCPReady]) || (self.secondaryTransportType == SDLSecondaryTransportTypeIAP))) {
+ [self.stateMachine transitionToState:SDLSecondaryTransportStateConnecting];
}
}
diff --git a/SmartDeviceLink/SDLTCPTransport.m b/SmartDeviceLink/SDLTCPTransport.m
index f56641fe7..fe642b9c0 100644
--- a/SmartDeviceLink/SDLTCPTransport.m
+++ b/SmartDeviceLink/SDLTCPTransport.m
@@ -309,24 +309,23 @@ NSTimeInterval ConnectionTimeoutSecs = 30.0;
NSError *error;
switch (stream.streamError.code) {
case ECONNREFUSED: {
- SDLLogD(@"TCP connection error: ECONNREFUSED");
+ SDLLogE(@"TCP connection error: ECONNREFUSED (connection refused)");
error = [NSError sdl_transport_connectionRefusedError];
} break;
case ETIMEDOUT: {
- SDLLogD(@"TCP connection error: ETIMEDOUT");
+ SDLLogE(@"TCP connection error: ETIMEDOUT (connection timed out)");
error = [NSError sdl_transport_connectionTimedOutError];
} break;
case ENETDOWN: {
- SDLLogD(@"TCP connection error: ENETDOWN");
+ SDLLogE(@"TCP connection error: ENETDOWN (network down)");
error = [NSError sdl_transport_networkDownError];
} break;
case ENETUNREACH: {
- // This is just for safe. I did not observe ENETUNREACH error on iPhone.
- SDLLogD(@"TCP connection error: ENETUNREACH");
+ SDLLogE(@"TCP connection error: ENETUNREACH (network unreachable)");
error = [NSError sdl_transport_networkDownError];
} break;
default: {
- SDLLogD(@"TCP connection error: unknown error %ld", (long)stream.streamError.code);
+ SDLLogE(@"TCP connection error: unknown error %ld", (long)stream.streamError.code);
error = [NSError sdl_transport_unknownError];
} break;
}
@@ -338,6 +337,7 @@ NSTimeInterval ConnectionTimeoutSecs = 30.0;
}
- (void)sdl_onStreamEnd:(NSStream *)stream {
+ SDLLogD(@"Stream ended");
NSAssert([[NSThread currentThread] isEqual:self.ioThread], @"sdl_onStreamEnd is called on a wrong thread!");
[self sdl_cancelIOThread];
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleManagerSpec.m
index 4328d3af3..4e136dfa0 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleManagerSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleManagerSpec.m
@@ -39,6 +39,7 @@
#import "SDLUnregisterAppInterface.h"
#import "SDLUnregisterAppInterfaceResponse.h"
#import "SDLVersion.h"
+#import "SDLVideoStreamingState.h"
// Ignore the deprecated proxy methods
@@ -627,6 +628,37 @@ describe(@"a lifecycle manager", ^{
});
});
});
+
+ describe(@"receiving a video state change", ^{
+ __block SDLOnHMIStatus *testHMIStatus = nil;
+ __block SDLVideoStreamingState testVideoStreamingState = nil;
+ __block SDLAudioStreamingState oldVideoStreamingState = nil;
+
+ beforeEach(^{
+ oldVideoStreamingState = testManager.videoStreamingState;
+ testHMIStatus = [[SDLOnHMIStatus alloc] init];
+ });
+
+ context(@"a not audible audio state", ^{
+ beforeEach(^{
+ testVideoStreamingState = SDLVideoStreamingStateNotStreamable;
+ testHMIStatus.videoStreamingState = testVideoStreamingState;
+
+ [testManager.notificationDispatcher postRPCNotificationNotification:SDLDidChangeHMIStatusNotification notification:testHMIStatus];
+ });
+
+ it(@"should set the audio state", ^{
+ expect(testManager.videoStreamingState).toEventually(equal(testVideoStreamingState));
+ });
+
+ it(@"should call the delegate", ^{
+ // Since notifications are sent to SDLManagerDelegate observers on the main thread, force the block to execute manually on the main thread. If this is not done, the test case may fail.
+ [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
+
+ OCMVerify([testManager.delegate videoStreamingState:oldVideoStreamingState didChangetoState:testVideoStreamingState]);
+ });
+ });
+ });
describe(@"receiving a system context change", ^{
__block SDLOnHMIStatus *testHMIStatus = nil;
diff --git a/SmartDeviceLinkTests/ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m
index 1a322e373..a8901a2a0 100644
--- a/SmartDeviceLinkTests/ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m
+++ b/SmartDeviceLinkTests/ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m
@@ -215,10 +215,7 @@ describe(@"SendRPCRequest Tests", ^ {
context(@"During V1 session", ^ {
it(@"Should send the correct data", ^ {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[[[mockRequest stub] andReturn:dictionaryV1] ignoringNonObjectArgs] serializeAsDictionary:1];
-#pragma clang diagnostic pop
SDLProtocol* testProtocol = [[SDLProtocol alloc] init];
SDLV1ProtocolHeader *testHeader = [[SDLV1ProtocolHeader alloc] init];
@@ -255,10 +252,7 @@ describe(@"SendRPCRequest Tests", ^ {
context(@"During V2 session", ^ {
it(@"Should send the correct data bulk data when bulk data is available", ^ {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[[[mockRequest stub] andReturn:dictionaryV2] ignoringNonObjectArgs] serializeAsDictionary:2];
-#pragma clang diagnostic pop
[[[mockRequest stub] andReturn:@0x98765] correlationID];
[[[mockRequest stub] andReturn:@"DeleteCommand"] name];
[[[mockRequest stub] andReturn:[NSData dataWithBytes:"COMMAND" length:strlen("COMMAND")]] bulkData];
diff --git a/SmartDeviceLinkTests/ProxySpecs/SDLSecondaryTransportManagerSpec.m b/SmartDeviceLinkTests/ProxySpecs/SDLSecondaryTransportManagerSpec.m
index d520a367e..2c4abf093 100644
--- a/SmartDeviceLinkTests/ProxySpecs/SDLSecondaryTransportManagerSpec.m
+++ b/SmartDeviceLinkTests/ProxySpecs/SDLSecondaryTransportManagerSpec.m
@@ -13,14 +13,18 @@
#import "SDLControlFramePayloadRegisterSecondaryTransportNak.h"
#import "SDLControlFramePayloadRPCStartServiceAck.h"
#import "SDLControlFramePayloadTransportEventUpdate.h"
+#import "SDLHMILevel.h"
#import "SDLIAPTransport.h"
#import "SDLNotificationConstants.h"
+#import "SDLRPCNotificationNotification.h"
#import "SDLProtocol.h"
#import "SDLSecondaryTransportManager.h"
#import "SDLStateMachine.h"
#import "SDLTCPTransport.h"
#import "SDLV2ProtocolMessage.h"
#import "SDLFakeSecurityManager.h"
+#import "SDLHMILevel.h"
+#import "SDLOnHMIStatus.h"
/* copied from SDLSecondaryTransportManager.m */
typedef NSNumber SDLServiceTypeBox;
@@ -55,7 +59,7 @@ static const int TCPPortUnspecified = -1;
@property (strong, nonatomic) NSMutableDictionary<SDLServiceTypeBox *, SDLTransportClassBox *> *streamingServiceTransportMap;
@property (strong, nonatomic, nullable) NSString *ipAddress;
@property (assign, nonatomic) int tcpPort;
-@property (assign, nonatomic, getter=isAppReady) BOOL appReady;
+@property (strong, nonatomic, nullable) SDLHMILevel currentHMILevel;
@end
@@ -137,6 +141,15 @@ describe(@"the secondary transport manager ", ^{
__block id<SDLTransportType> testPrimaryTransport = nil;
__block id testStreamingProtocolDelegate = nil;
+ __block void (^sendNotificationForHMILevel)(SDLHMILevel hmiLevel) = ^(SDLHMILevel hmiLevel) {
+ SDLOnHMIStatus *hmiStatus = [[SDLOnHMIStatus alloc] init];
+ hmiStatus.hmiLevel = hmiLevel;
+ SDLRPCNotificationNotification *notification = [[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:self rpcNotification:hmiStatus];
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
+
+ [NSThread sleepForTimeInterval:0.3];
+ };
+
beforeEach(^{
[SDLSecondaryTransportManager swapGetAppStateMethod];
[SDLTCPTransport swapConnectionMethods];
@@ -413,7 +426,7 @@ describe(@"the secondary transport manager ", ^{
testStartServiceACKPayload = [[SDLControlFramePayloadRPCStartServiceAck alloc] initWithHashId:testHashId mtu:testMtu authToken:nil protocolVersion:testProtocolVersion secondaryTransports:testSecondaryTransports audioServiceTransports:testAudioServiceTransports videoServiceTransports:testVideoServiceTransports];
testStartServiceACKMessage = [[SDLV2ProtocolMessage alloc] initWithHeader:testStartServiceACKHeader andPayload:testStartServiceACKPayload.data];
- manager.appReady = YES;
+ manager.currentHMILevel = SDLHMILevelFull;
});
it(@"should configure its properties and immediately transition to Connecting state", ^{
@@ -507,27 +520,23 @@ describe(@"the secondary transport manager ", ^{
});
});
- context(@"before the security manager is set by register app interface response", ^{
+ context(@"before the app context is HMI FULL", ^{
it(@"should stay in state Configured", ^{
expect(manager.stateMachine.currentState).to(equal(SDLSecondaryTransportStateConfigured));
- expect(manager.secondaryProtocol.securityManager).to(beNil());
- expect(manager.isAppReady).to(equal(NO));
+ expect(manager.currentHMILevel).to(beNil());
OCMVerifyAll(testStreamingProtocolDelegate);
});
});
- context(@"after the security manager is set by register app interface response", ^{
+ context(@"app becomes HMI FULL", ^{
beforeEach(^{
- testPrimaryProtocol.securityManager = OCMClassMock([SDLFakeSecurityManager class]);
- // By the time this notification is recieved the RAIR should have been sent and the security manager should exist if available
- [[NSNotificationCenter defaultCenter] postNotificationName:SDLDidBecomeReady object:nil];
+ sendNotificationForHMILevel(SDLHMILevelFull);
});
it(@"should transition to Connecting state", ^{
expect(manager.stateMachine.currentState).to(equal(SDLSecondaryTransportStateConnecting));
- expect(manager.secondaryProtocol.securityManager).to(equal(testPrimaryProtocol.securityManager));
- expect(manager.isAppReady).to(equal(YES));
+ expect(manager.currentHMILevel).to(equal(SDLHMILevelFull));
OCMVerifyAll(testStreamingProtocolDelegate);
});
@@ -561,7 +570,7 @@ describe(@"the secondary transport manager ", ^{
});
});
- describe(@"and Transport Event Update is received", ^{
+ describe(@"and a Transport Event Update has been received", ^{
__block SDLProtocolHeader *testTransportEventUpdateHeader = nil;
__block SDLProtocolMessage *testTransportEventUpdateMessage = nil;
__block SDLControlFramePayloadTransportEventUpdate *testTransportEventUpdatePayload = nil;
@@ -584,24 +593,23 @@ describe(@"the secondary transport manager ", ^{
});
- context(@"before the security manager is set by register app interface response", ^{
+ context(@"before the app context is HMI FULL", ^{
it(@"should stay in Configured state", ^{
expect(manager.stateMachine.currentState).to(equal(SDLSecondaryTransportStateConfigured));
- expect(manager.secondaryProtocol.securityManager).to(beNil());
+ expect(manager.currentHMILevel).to(beNil());
+
OCMVerifyAll(testStreamingProtocolDelegate);
});
});
- context(@"after the security manager is set by register app interface response", ^{
+ context(@"app becomes HMI FULL", ^{
beforeEach(^{
- testPrimaryProtocol.securityManager = OCMClassMock([SDLFakeSecurityManager class]);
- // By the time this notification is recieved the RAIR should have been sent and the security manager should exist if available
- [[NSNotificationCenter defaultCenter] postNotificationName:SDLDidBecomeReady object:nil];
+ sendNotificationForHMILevel(SDLHMILevelFull);
});
it(@"should transition to Connecting", ^{
expect(manager.stateMachine.currentState).to(equal(SDLSecondaryTransportStateConnecting));
- expect(manager.secondaryProtocol.securityManager).to(equal(testPrimaryProtocol.securityManager));
+ expect(manager.currentHMILevel).to(equal(SDLHMILevelFull));
OCMVerifyAll(testStreamingProtocolDelegate);
});
@@ -731,7 +739,6 @@ describe(@"the secondary transport manager ", ^{
[NSThread sleepForTimeInterval:0.1];
expect(manager.stateMachine.currentState).to(equal(SDLSecondaryTransportStateReconnecting));
- expect(manager.isAppReady).to(equal(NO));
OCMVerifyAll(testStreamingProtocolDelegate);
});
});
@@ -776,7 +783,7 @@ describe(@"the secondary transport manager ", ^{
beforeEach(^{
testTcpIpAddress = @"172.16.12.34";
testTcpPort = 12345;
- manager.appReady = YES;
+ manager.currentHMILevel = SDLHMILevelFull;
testTransportEventUpdatePayload = [[SDLControlFramePayloadTransportEventUpdate alloc] initWithTcpIpAddress:testTcpIpAddress tcpPort:testTcpPort];
testTransportEventUpdateMessage = [[SDLV2ProtocolMessage alloc] initWithHeader:testTransportEventUpdateHeader andPayload:testTransportEventUpdatePayload.data];
@@ -864,7 +871,7 @@ describe(@"the secondary transport manager ", ^{
manager.secondaryTransportType = SDLTransportSelectionTCP;
manager.ipAddress = @"192.168.1.1";
manager.tcpPort = 12345;
- manager.appReady = YES;
+ manager.currentHMILevel = SDLHMILevelFull;
testTransportEventUpdateHeader = [SDLProtocolHeader headerForVersion:5];
testTransportEventUpdateHeader.frameType = SDLFrameTypeControl;
@@ -1023,7 +1030,6 @@ describe(@"the secondary transport manager ", ^{
manager.secondaryTransportType = SDLTransportSelectionTCP;
manager.ipAddress = @"192.168.1.1";
manager.tcpPort = 12345;
- manager.appReady = YES;
testTransportEventUpdateHeader = [SDLProtocolHeader headerForVersion:5];
testTransportEventUpdateHeader.frameType = SDLFrameTypeControl;
@@ -1053,6 +1059,7 @@ describe(@"the secondary transport manager ", ^{
beforeEach(^{
testTcpIpAddress = @"172.16.12.34";
testTcpPort = 12345;
+ manager.currentHMILevel = SDLHMILevelFull;
testTransportEventUpdatePayload = [[SDLControlFramePayloadTransportEventUpdate alloc] initWithTcpIpAddress:testTcpIpAddress tcpPort:testTcpPort];
testTransportEventUpdateMessage = [[SDLV2ProtocolMessage alloc] initWithHeader:testTransportEventUpdateHeader andPayload:testTransportEventUpdatePayload.data];
diff --git a/SmartDeviceLinkTests/RPCSpecs/SuperclassSpecs/SDLRPCStructSpec.m b/SmartDeviceLinkTests/RPCSpecs/SuperclassSpecs/SDLRPCStructSpec.m
index 8dd6eaf99..1b2c4d348 100644
--- a/SmartDeviceLinkTests/RPCSpecs/SuperclassSpecs/SDLRPCStructSpec.m
+++ b/SmartDeviceLinkTests/RPCSpecs/SuperclassSpecs/SDLRPCStructSpec.m
@@ -14,13 +14,10 @@ QuickSpecBegin(SDLRPCStructSpec)
describe(@"SerializeAsDictionary Tests", ^ {
it(@"Should serialize correctly", ^ {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
NSMutableDictionary<NSString *, id> *dict = [@{@"Key":@"Value", @"Answer":@42, @"Struct":[[SDLRPCStruct alloc] initWithDictionary:[@{@"Array":@[@1, @1, @1, @1]} mutableCopy]]} mutableCopy];
SDLRPCStruct* testStruct = [[SDLRPCStruct alloc] initWithDictionary:dict];
expect([testStruct serializeAsDictionary:2]).to(equal([@{@"Key":@"Value", @"Answer":@42, @"Struct":@{@"Array":@[@1, @1, @1, @1]}} mutableCopy]));
-#pragma clang diagnostic pop
});
});