Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Security manager set for secondary protocol #1482

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 20 additions & 4 deletions SmartDeviceLink/SDLSecondaryTransportManager.m
Expand Up @@ -18,6 +18,7 @@
#import "SDLLogMacros.h"
#import "SDLProtocol.h"
#import "SDLProtocolHeader.h"
#import "SDLNotificationConstants.h"
#import "SDLSecondaryTransportPrimaryProtocolHandler.h"
#import "SDLStateMachine.h"
#import "SDLTCPTransport.h"
Expand Down Expand Up @@ -94,6 +95,8 @@ @interface SDLSecondaryTransportManager ()
@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;

@end

Expand All @@ -119,6 +122,8 @@ - (instancetype)initWithStreamingProtocolDelegate:(id<SDLStreamingProtocolDelega
@(SDLServiceTypeVideo):@(SDLTransportClassInvalid)} mutableCopy];
_tcpPort = TCPPortUnspecified;

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidBecomeReady) name:SDLDidBecomeReady object:nil];

return self;
}

Expand Down Expand Up @@ -224,8 +229,8 @@ - (void)didEnterStateStarted {
}

- (void)didEnterStateConfigured {
if ((self.secondaryTransportType == SDLSecondaryTransportTypeTCP && [self sdl_isTCPReady])
|| self.secondaryTransportType == SDLSecondaryTransportTypeIAP) {
if ((self.secondaryTransportType == SDLSecondaryTransportTypeTCP && [self sdl_isTCPReady] && self.isAppReady)
|| (self.secondaryTransportType == SDLSecondaryTransportTypeIAP && self.isAppReady)) {
[self.stateMachine transitionToState:SDLSecondaryTransportStateConnecting];
}
}
Expand Down Expand Up @@ -277,6 +282,7 @@ - (void)willTransitionFromStateRegisteredToStateStopped {
}

- (void)didEnterStateReconnecting {
self.appReady = NO;
__weak typeof(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(RetryConnectionDelay * NSEC_PER_SEC)), _stateMachineQueue, ^{
if ([weakSelf.stateMachine isCurrentState:SDLSecondaryTransportStateReconnecting]) {
Expand Down Expand Up @@ -366,7 +372,7 @@ - (void)sdl_handleTransportEventUpdate {
return;
}

if ([self.stateMachine isCurrentState:SDLSecondaryTransportStateConfigured] && [self sdl_isTCPReady]) {
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.
Expand Down Expand Up @@ -507,6 +513,7 @@ - (BOOL)sdl_startTCPSecondaryTransport {
transport.delegate = protocol;
protocol.transport = transport;
[protocol.protocolDelegateTable addObject:self];
protocol.securityManager = self.primaryProtocol.securityManager;

self.secondaryProtocol = protocol;
self.secondaryTransport = transport;
Expand All @@ -529,6 +536,7 @@ - (BOOL)sdl_startIAPSecondaryTransport {
transport.delegate = protocol;
protocol.transport = transport;
[protocol.protocolDelegateTable addObject:self];
protocol.securityManager = self.primaryProtocol.securityManager;

self.secondaryProtocol = protocol;
self.secondaryTransport = transport;
Expand Down Expand Up @@ -648,7 +656,7 @@ - (void)sdl_onAppStateUpdated:(NSNotification *)notification {
}
} else if (notification.name == UIApplicationDidBecomeActiveNotification) {
if (([self.stateMachine isCurrentState:SDLSecondaryTransportStateConfigured])
&& self.secondaryTransportType == SDLSecondaryTransportTypeTCP && [self sdl_isTCPReady]) {
&& self.secondaryTransportType == SDLSecondaryTransportTypeTCP && [self sdl_isTCPReady] && self.isAppReady) {
SDLLogD(@"Resuming TCP transport since the app becomes foreground");
[self.stateMachine transitionToState:SDLSecondaryTransportStateConnecting];
}
Expand Down Expand Up @@ -705,6 +713,14 @@ - (SDLSecondaryTransportType)sdl_getTransportTypeFromProtocol:(SDLProtocol *)pro
}
}

- (void)appDidBecomeReady {
self.appReady = YES;
if (([self.stateMachine.currentState isEqualToString:SDLSecondaryTransportStateConfigured] && self.tcpPort != SDLControlFrameInt32NotFound && self.ipAddress != nil)
|| self.secondaryTransportType == SDLSecondaryTransportTypeIAP) {
[self.stateMachine transitionToState:SDLSecondaryTransportStateConnecting];
}
joeljfischer marked this conversation as resolved.
Show resolved Hide resolved
}

@end

NS_ASSUME_NONNULL_END
72 changes: 61 additions & 11 deletions SmartDeviceLinkTests/ProxySpecs/SDLSecondaryTransportManagerSpec.m
Expand Up @@ -14,11 +14,13 @@
#import "SDLControlFramePayloadRPCStartServiceAck.h"
#import "SDLControlFramePayloadTransportEventUpdate.h"
#import "SDLIAPTransport.h"
#import "SDLNotificationConstants.h"
#import "SDLProtocol.h"
#import "SDLSecondaryTransportManager.h"
#import "SDLStateMachine.h"
#import "SDLTCPTransport.h"
#import "SDLV2ProtocolMessage.h"
#import "SDLFakeSecurityManager.h"

/* copied from SDLSecondaryTransportManager.m */
typedef NSNumber SDLServiceTypeBox;
Expand All @@ -45,13 +47,15 @@ @interface SDLSecondaryTransportManager ()

// we need to reach to private properties for the tests
@property (assign, nonatomic) SDLSecondaryTransportType secondaryTransportType;
@property (nullable, strong, nonatomic) SDLProtocol *primaryProtocol;
@property (nullable, strong, nonatomic) id<SDLTransportType> secondaryTransport;
@property (nullable, strong, nonatomic) SDLProtocol *secondaryProtocol;
@property (strong, nonatomic, nonnull) NSArray<SDLTransportClassBox *> *transportsForAudioService;
@property (strong, nonatomic, nonnull) NSArray<SDLTransportClassBox *> *transportsForVideoService;
@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;

@end

Expand Down Expand Up @@ -409,6 +413,7 @@ + (void)swapConnectionMethods {

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;
});

it(@"should configure its properties and immediately transition to Connecting state", ^{
Expand Down Expand Up @@ -483,36 +488,58 @@ + (void)swapConnectionMethods {
});
});


describe(@"In Configured state", ^{
describe(@"if secondary transport is iAP", ^{
beforeEach(^{
// in this case we assume the primary transport is TCP
testPrimaryProtocol = [[SDLProtocol alloc] init];
testPrimaryTransport = [[SDLTCPTransport alloc] init];
testPrimaryProtocol.transport = testPrimaryTransport;

dispatch_sync(testStateMachineQueue, ^{
[manager startWithPrimaryProtocol:testPrimaryProtocol];
});

manager.secondaryTransportType = SDLTransportSelectionIAP;
});

it(@"should transition to Connecting state", ^{
// setToState cannot be used here, as the method will set the state after calling didEnterStateConfigured
dispatch_sync(testStateMachineQueue, ^{
[manager.stateMachine transitionToState:SDLSecondaryTransportStateConfigured];
});
});

expect(manager.stateMachine.currentState).to(equal(SDLSecondaryTransportStateConnecting));
OCMVerifyAll(testStreamingProtocolDelegate);
context(@"before the security manager is set by register app interface response", ^{
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));

OCMVerifyAll(testStreamingProtocolDelegate);
});
});

context(@"after the security manager is set by register app interface response", ^{
beforeEach(^{
testPrimaryProtocol.securityManager = OCMClassMock([SDLFakeSecurityManager class]);
joeljfischer marked this conversation as resolved.
Show resolved Hide resolved
// 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];
joeljfischer marked this conversation as resolved.
Show resolved Hide resolved
});

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));

OCMVerifyAll(testStreamingProtocolDelegate);
});
});
});

describe(@"if secondary transport is TCP", ^{
beforeEach(^{
testPrimaryProtocol = [[SDLProtocol alloc] init];
testPrimaryTransport = [[SDLIAPTransport alloc] init];
testPrimaryProtocol.transport = testPrimaryTransport;

dispatch_sync(testStateMachineQueue, ^{
[manager startWithPrimaryProtocol:testPrimaryProtocol];
});
Expand All @@ -529,6 +556,7 @@ + (void)swapConnectionMethods {
describe(@"and Transport Event Update is not received", ^{
it(@"should stay in Configured state", ^{
expect(manager.stateMachine.currentState).to(equal(SDLSecondaryTransportStateConfigured));

OCMVerifyAll(testStreamingProtocolDelegate);
});
});
Expand All @@ -550,14 +578,33 @@ + (void)swapConnectionMethods {

testTransportEventUpdatePayload = [[SDLControlFramePayloadTransportEventUpdate alloc] initWithTcpIpAddress:testTcpIpAddress tcpPort:testTcpPort];
testTransportEventUpdateMessage = [[SDLV2ProtocolMessage alloc] initWithHeader:testTransportEventUpdateHeader andPayload:testTransportEventUpdatePayload.data];
});

it(@"should transition to Connecting state", ^{
[testPrimaryProtocol handleBytesFromTransport:testTransportEventUpdateMessage.data];
[NSThread sleepForTimeInterval:0.1];

expect(manager.stateMachine.currentState).to(equal(SDLSecondaryTransportStateConnecting));
OCMVerifyAll(testStreamingProtocolDelegate);
});

context(@"before the security manager is set by register app interface response", ^{
it(@"should stay in Configured state", ^{
expect(manager.stateMachine.currentState).to(equal(SDLSecondaryTransportStateConfigured));
expect(manager.secondaryProtocol.securityManager).to(beNil());
OCMVerifyAll(testStreamingProtocolDelegate);
});
});

context(@"after the security manager is set by register app interface response", ^{
beforeEach(^{
testPrimaryProtocol.securityManager = OCMClassMock([SDLFakeSecurityManager class]);
joeljfischer marked this conversation as resolved.
Show resolved Hide resolved
// 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];
});

it(@"should transition to Connecting", ^{
expect(manager.stateMachine.currentState).to(equal(SDLSecondaryTransportStateConnecting));
expect(manager.secondaryProtocol.securityManager).to(equal(testPrimaryProtocol.securityManager));

OCMVerifyAll(testStreamingProtocolDelegate);
});
});
});
});
Expand Down Expand Up @@ -585,7 +632,6 @@ + (void)swapConnectionMethods {
});
});


describe(@"In Connecting state", ^{
__block SDLProtocol *secondaryProtocol = nil;
__block id testSecondaryProtocolMock = nil;
Expand Down Expand Up @@ -685,6 +731,7 @@ + (void)swapConnectionMethods {
[NSThread sleepForTimeInterval:0.1];

expect(manager.stateMachine.currentState).to(equal(SDLSecondaryTransportStateReconnecting));
expect(manager.isAppReady).to(equal(NO));
OCMVerifyAll(testStreamingProtocolDelegate);
});
});
Expand Down Expand Up @@ -729,6 +776,7 @@ + (void)swapConnectionMethods {
beforeEach(^{
testTcpIpAddress = @"172.16.12.34";
testTcpPort = 12345;
manager.appReady = YES;

testTransportEventUpdatePayload = [[SDLControlFramePayloadTransportEventUpdate alloc] initWithTcpIpAddress:testTcpIpAddress tcpPort:testTcpPort];
testTransportEventUpdateMessage = [[SDLV2ProtocolMessage alloc] initWithHeader:testTransportEventUpdateHeader andPayload:testTransportEventUpdatePayload.data];
Expand Down Expand Up @@ -816,6 +864,7 @@ + (void)swapConnectionMethods {
manager.secondaryTransportType = SDLTransportSelectionTCP;
manager.ipAddress = @"192.168.1.1";
manager.tcpPort = 12345;
manager.appReady = YES;

testTransportEventUpdateHeader = [SDLProtocolHeader headerForVersion:5];
testTransportEventUpdateHeader.frameType = SDLFrameTypeControl;
Expand Down Expand Up @@ -974,6 +1023,7 @@ + (void)swapConnectionMethods {
manager.secondaryTransportType = SDLTransportSelectionTCP;
manager.ipAddress = @"192.168.1.1";
manager.tcpPort = 12345;
manager.appReady = YES;

testTransportEventUpdateHeader = [SDLProtocolHeader headerForVersion:5];
testTransportEventUpdateHeader.frameType = SDLFrameTypeControl;
Expand Down