diff options
author | Michael Kinney <michaelakinney@comcast.net> | 2021-01-21 19:24:03 -0500 |
---|---|---|
committer | Michael Kinney <michaelakinney@comcast.net> | 2021-01-21 19:24:03 -0500 |
commit | d81a887102fa0b948e8b4c057ed1e442a72d1bf2 (patch) | |
tree | c266e7093fb2a9531fd0a34c9d634522b9271677 | |
parent | 5f8fd6a2d67050f856369039d4cfe72265b9a803 (diff) | |
download | sdl_ios-d81a887102fa0b948e8b4c057ed1e442a72d1bf2.tar.gz |
refactor sdliapcontrolsession
-rw-r--r-- | SmartDeviceLink/private/SDLIAPControlSession.h | 27 | ||||
-rw-r--r-- | SmartDeviceLink/private/SDLIAPControlSession.m | 209 | ||||
-rw-r--r-- | SmartDeviceLink/private/SDLIAPControlSessionDelegate.h | 2 |
3 files changed, 72 insertions, 166 deletions
diff --git a/SmartDeviceLink/private/SDLIAPControlSession.h b/SmartDeviceLink/private/SDLIAPControlSession.h index 03940ef98..5e65fbe25 100644 --- a/SmartDeviceLink/private/SDLIAPControlSession.h +++ b/SmartDeviceLink/private/SDLIAPControlSession.h @@ -7,7 +7,6 @@ // #import <Foundation/Foundation.h> - #import "SDLIAPSession.h" @class EAAccessory; @@ -21,19 +20,39 @@ NS_ASSUME_NONNULL_BEGIN * * When the protocol string is received from Core, the control session is closed as a new session with Core must be established with the received protocol string. Core has ~10 seconds to send the protocol string, otherwise the control session is closed and new attempt is made to establish a control session with Core. */ -@interface SDLIAPControlSession : SDLIAPSession +@interface SDLIAPControlSession: NSObject <SDLIAPSessionDelegate> - (instancetype)init NS_UNAVAILABLE; /** * Creates a new control session. * - * @param accessory The accessory to connect to. + * @param accessory The accessory to connect to. * @param delegate The control session delegate * @return A SDLIAPControlSession object */ -- (instancetype)initWithAccessory:(nullable EAAccessory *)accessory delegate:(id<SDLIAPControlSessionDelegate>)delegate; +- (instancetype)initWithAccessory:(nullable EAAccessory *)accessory delegate:(id<SDLIAPControlSessionDelegate>)delegate forProtocol:(NSString *)protocol; + +// document +- (void) closeSession; + +/** + * Returns whether the session has open I/O streams. + */ +@property (assign, nonatomic, readonly, getter=isSessionInProgress) BOOL sessionInProgress; + +/** + * The accessory with which to open a session. + */ +@property (nullable, strong, nonatomic, readonly) EAAccessory *accessory; + +/** + * The unique ID assigned to the session between the app and accessory. If no session exists the value will be 0. + */ +@property (assign, nonatomic, readonly) NSUInteger connectionID; @end NS_ASSUME_NONNULL_END + + diff --git a/SmartDeviceLink/private/SDLIAPControlSession.m b/SmartDeviceLink/private/SDLIAPControlSession.m index 50d7a98e4..c89cc2fb5 100644 --- a/SmartDeviceLink/private/SDLIAPControlSession.m +++ b/SmartDeviceLink/private/SDLIAPControlSession.m @@ -22,206 +22,94 @@ NS_ASSUME_NONNULL_BEGIN int const ProtocolIndexTimeoutSeconds = 10; @interface SDLIAPControlSession () - @property (nullable, strong, nonatomic) SDLTimer *protocolIndexTimer; @property (weak, nonatomic) id<SDLIAPControlSessionDelegate> delegate; - +@property (nullable, nonatomic, strong) SDLIAPSession *iapSession; @end @implementation SDLIAPControlSession #pragma mark - Session lifecycle -- (instancetype)initWithAccessory:(nullable EAAccessory *)accessory delegate:(id<SDLIAPControlSessionDelegate>)delegate { - SDLLogV(@"SDLIAPControlSession init"); - - self = [super initWithAccessory:accessory forProtocol:ControlProtocolString]; - if (!self) { return nil; } - +- (instancetype)initWithAccessory:(nullable EAAccessory *)accessory delegate:(id<SDLIAPControlSessionDelegate>)delegate forProtocol:(NSString *)protocol { + SDLLogD(@"SDLIAPControlSession init with protocol %@ and accessory %@", protocol, accessory); + self = [super init]; + _iapSession = [[SDLIAPSession alloc] initWithAccessory:accessory forProtocol:protocol iAPSessionDelegate:self]; _protocolIndexTimer = nil; _delegate = delegate; + SDLLogD(@"SDLIAPControlSession Wait for the protocol string from Core, setting timeout timer for %d seconds", ProtocolIndexTimeoutSeconds); + self.protocolIndexTimer = [self sdl_createControlSessionProtocolIndexStringDataTimeoutTimer]; return self; } -#pragma mark Start - -- (void)startSession { - if (self.accessory == nil) { - SDLLogW(@"There is no control session in progress, attempting to create a new control session."); - [self.delegate controlSessionShouldRetry]; - } else { - SDLLogD(@"Starting a control session with accessory (%@)", self.accessory.name); - __weak typeof(self) weakSelf = self; - [self sdl_startStreamsWithCompletionHandler:^(BOOL success) { - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!success) { - SDLLogW(@"Control session failed to setup with accessory: %@. Attempting to create a new control session", strongSelf.accessory); - [strongSelf destroySessionWithCompletionHandler:^{ - __strong typeof(weakSelf) strongSelf = weakSelf; - [strongSelf.delegate controlSessionShouldRetry]; - }]; - } else { - SDLLogD(@"Waiting for the protocol string from Core, setting timeout timer for %d seconds", ProtocolIndexTimeoutSeconds); - strongSelf.protocolIndexTimer = [strongSelf sdl_createControlSessionProtocolIndexStringDataTimeoutTimer]; - } - }]; - } +- (void) closeSession { + [self.iapSession closeSession]; } -/// Opens the input and output streams for the session on the main thread. -/// @discussion We must close the input/output streams from the same thread that owns the streams' run loop, otherwise if the streams are closed from another thread a random crash may occur. Since only a small amount of data will be transmitted on this stream before it is closed, we will open and close the streams on the main thread instead of creating a separate thread. -- (void)sdl_startStreamsWithCompletionHandler:(void (^)(BOOL success))completionHandler { - if (![super createSession]) { - return completionHandler(NO); - } - - __weak typeof(self) weakSelf = self; - dispatch_async(dispatch_get_main_queue(), ^{ - __strong typeof(weakSelf) strongSelf = weakSelf; - - SDLLogD(@"Created the control session successfully"); - [super startStream:strongSelf.eaSession.outputStream]; - [super startStream:strongSelf.eaSession.inputStream]; - - return completionHandler(YES); - }); +- (nullable EAAccessory *) accessory { + return self.iapSession.accessory; } - -#pragma mark Stop - -/// Makes sure the session is closed and destroyed on the main thread. -/// @param disconnectCompletionHandler Handler called when the session has disconnected -- (void)destroySessionWithCompletionHandler:(void (^)(void))disconnectCompletionHandler { - SDLLogD(@"Destroying the control session"); - dispatch_async(dispatch_get_main_queue(), ^{ - [self sdl_stopAndDestroySession]; - return disconnectCompletionHandler(); - }); +- (NSUInteger)connectionID { + return self.iapSession.connectionID; } -/// Closes the session streams and then destroys the session. -- (void)sdl_stopAndDestroySession { - NSAssert(NSThread.isMainThread, @"%@ must only be called on the main thread", NSStringFromSelector(_cmd)); - - [super stopStream:self.eaSession.outputStream]; - [super stopStream:self.eaSession.inputStream]; - [super cleanupClosedSession]; +- (BOOL)isSessionInProgress { + return [self.iapSession isSessionInProgress]; } - #pragma mark - NSStreamDelegate -/** - * Handles events on the input/output streams of the open session. - * - * @param stream The stream (either input or output) that the event occured on - * @param eventCode The stream event code - */ -- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode { - switch (eventCode) { - case NSStreamEventOpenCompleted: { - [self sdl_streamDidOpen:stream]; - break; - } - case NSStreamEventHasBytesAvailable: { - [self sdl_streamHasBytesAvailable:(NSInputStream *)stream]; - break; - } - case NSStreamEventErrorOccurred: { - [self sdl_streamDidError:stream]; - break; - } - case NSStreamEventEndEncountered: { - [self sdl_streamDidEnd:stream]; - break; - } - case NSStreamEventNone: - case NSStreamEventHasSpaceAvailable: - default: { - break; - } - } -} -/** - * Called when the session gets a `NSStreamEventOpenCompleted`. When both the input and output streams open, start a timer to get data from Core within a certain timeframe. - * - * @param stream The stream that got the event code. - */ -- (void)sdl_streamDidOpen:(NSStream *)stream { - if (stream == [self.eaSession outputStream]) { - SDLLogD(@"Control session output stream opened"); - self.isOutputStreamOpen = YES; - } else if (stream == [self.eaSession inputStream]) { - SDLLogD(@"Control session input stream opened"); - self.isInputStreamOpen = YES; - } - - // When both streams are open, session initialization is complete. Let the delegate know. - if (self.isInputStreamOpen && self.isOutputStreamOpen) { - SDLLogV(@"Control session I/O streams opened for protocol: %@", self.protocolString); +- (void) streamsDidOpen { + SDLLogD(@"SDLIAPControlSession streams opened for control session instance %@", self); + if (self.delegate != nil) { [self sdl_startControlSessionProtocolIndexStringDataTimeoutTimer]; } } -/** - * Called when the session gets a `NSStreamEventEndEncountered` event code. The current session is closed and a new session is attempted. - */ -- (void)sdl_streamDidEnd:(NSStream *)stream { - SDLLogD(@"Control stream ended"); - - // End events come in pairs, only perform this once per set. - [self.protocolIndexTimer cancel]; - - __weak typeof(self) weakSelf = self; - [self destroySessionWithCompletionHandler:^{ - __strong typeof(weakSelf) strongSelf = weakSelf; - [strongSelf.delegate controlSessionShouldRetry]; - }]; +- (void) streamsDidEnd { + SDLLogD(@"SDLIAPControlSession EASession stream ended"); + if (self.delegate != nil) { + [self.delegate controlSessionDidEnd]; + } } +- (void)streamHasSpaceToWrite {} + /** * Called when the session gets a `NSStreamEventHasBytesAvailable` event code. A protocol string is created from the received data. Since a new session needs to be established with the protocol string, the current session is closed and a new session is created. */ -- (void)sdl_streamHasBytesAvailable:(NSInputStream *)inputStream { - SDLLogV(@"Control stream received data"); - +- (void)streamHasBytesAvailable:(NSInputStream *)inputStream { + SDLLogD(@"SDLIAPControlSession EASession stream received data"); + // Read in the stream a single byte at a time uint8_t buf[1]; NSInteger len = [inputStream read:buf maxLength:1]; if (len <= 0) { - SDLLogV(@"No data in the control stream"); + SDLLogD(@"No data in the control stream"); return; } - - // If we have data from the control stream, use the data to create the protocol string needed to establish the data session. + + // If we have data from the control stream, use the data to create the protocol string needed to establish a data session. NSString *indexedProtocolString = [NSString stringWithFormat:@"%@%@", IndexedProtocolStringPrefix, @(buf[0])]; - SDLLogD(@"Control Stream will switch to protocol %@", indexedProtocolString); - - // Destroy the control session as it is no longer needed, and then create the data session. - __weak typeof(self) weakSelf = self; - [self destroySessionWithCompletionHandler:^{ - __strong typeof(weakSelf) strongSelf = weakSelf; - if (strongSelf.accessory.isConnected) { - [strongSelf.protocolIndexTimer cancel]; - [strongSelf.delegate controlSession:strongSelf didReceiveProtocolString:indexedProtocolString]; - } - }]; + SDLLogD(@"SDLIAPControlSession EASession Stream will switch to protocol %@", indexedProtocolString); + + [self.protocolIndexTimer cancel]; + if (self.delegate != nil) { + [self.delegate controlSession:self didReceiveProtocolString:indexedProtocolString]; + } } /** * Called when the session gets a `NSStreamEventErrorOccurred` event code. The current session is closed and a new session is attempted. */ -- (void)sdl_streamDidError:(NSStream *)stream { - SDLLogE(@"Control stream error"); - +- (void)streamDidError { + SDLLogE(@"SDLIAPControlSession stream error"); [self.protocolIndexTimer cancel]; - __weak typeof(self) weakSelf = self; - [self destroySessionWithCompletionHandler:^{ - __strong typeof(weakSelf) strongSelf = weakSelf; - [strongSelf.delegate controlSessionShouldRetry]; - }]; + if (self.delegate != nil) { + [self.delegate controlSessionDidEnd]; + } } #pragma mark - Timer @@ -233,17 +121,14 @@ int const ProtocolIndexTimeoutSeconds = 10; */ - (SDLTimer *)sdl_createControlSessionProtocolIndexStringDataTimeoutTimer { SDLTimer *protocolIndexTimer = [[SDLTimer alloc] initWithDuration:ProtocolIndexTimeoutSeconds repeat:NO]; - __weak typeof(self) weakSelf = self; void (^elapsedBlock)(void) = ^{ __strong typeof(weakSelf) strongSelf = weakSelf; - SDLLogW(@"Control session failed to get the protocol string from Core after %d seconds, retrying.", ProtocolIndexTimeoutSeconds); - [strongSelf destroySessionWithCompletionHandler:^{ - __strong typeof(weakSelf) strongSelf = weakSelf; - [strongSelf.delegate controlSessionShouldRetry]; - }]; + SDLLogW(@"SDLIAPControlSession failed to get the protocol string from Core after %d seconds, retrying.", ProtocolIndexTimeoutSeconds); + if (self.delegate != nil) { + [strongSelf.delegate controlSessionDidEnd]; + } }; - protocolIndexTimer.elapsedBlock = elapsedBlock; return protocolIndexTimer; } @@ -259,3 +144,5 @@ int const ProtocolIndexTimeoutSeconds = 10; @end NS_ASSUME_NONNULL_END + + diff --git a/SmartDeviceLink/private/SDLIAPControlSessionDelegate.h b/SmartDeviceLink/private/SDLIAPControlSessionDelegate.h index 67ba2b042..0b5946fdd 100644 --- a/SmartDeviceLink/private/SDLIAPControlSessionDelegate.h +++ b/SmartDeviceLink/private/SDLIAPControlSessionDelegate.h @@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN @protocol SDLIAPControlSessionDelegate <NSObject> -- (void)controlSessionShouldRetry; +- (void)controlSessionDidEnd; - (void)controlSession:(SDLIAPControlSession *)controlSession didReceiveProtocolString:(NSString *)protocolString; @end |