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

WIP: Fixing start session failure on legacy modules #1796

Closed
Closed
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
47 changes: 1 addition & 46 deletions SmartDeviceLink/private/SDLIAPTransport.m
Expand Up @@ -98,7 +98,7 @@ - (void)sdl_accessoryConnected:(NSNotification *)notification {
return;
}

double retryDelay = self.sdl_retryDelay;
double retryDelay = 0.0;
SDLLogD(@"Accessory Connected (%@), Opening in %0.03fs", notification.userInfo[EAAccessoryKey], retryDelay);

self.retryCounter = 0;
Expand Down Expand Up @@ -429,51 +429,6 @@ + (nullable NSString *)sdl_supportsRequiredProtocolStrings {
return nil;
}

#pragma mark Retry Delay

/**
* Generates a random number of seconds between 1.5 and 9.5 used to delay the retry control and data session attempts.
*
* @return A random number of seconds.
*/
- (double)sdl_retryDelay {
const double MinRetrySeconds = 1.5;
const double MaxRetrySeconds = 9.5;
double RetryRangeSeconds = MaxRetrySeconds - MinRetrySeconds;

static double appDelaySeconds = 0;

// HAX: This pull the app name and hashes it in an attempt to provide a more even distribution of retry delays. The evidence that this does so is anecdotal. A more ideal solution would be to use a list of known, installed SDL apps on the phone to try and deterministically generate an even delay.
if (appDelaySeconds == 0) {
NSString *appName = [[NSProcessInfo processInfo] processName];
if (appName == nil) {
appName = @"noname";
}

// Run the app name through an md5 hasher
const char *ptr = [appName UTF8String];
unsigned char md5Buffer[CC_MD5_DIGEST_LENGTH];
CC_MD5(ptr, (unsigned int)strlen(ptr), md5Buffer);

// Generate a string of the hex hash
NSMutableString *output = [NSMutableString stringWithString:@"0x"];
for (int i = 0; i < 8; i++) {
[output appendFormat:@"%02X", md5Buffer[i]];
}

// Transform the string into a number between 0 and 1
unsigned long long firstHalf;
NSScanner *pScanner = [NSScanner scannerWithString:output];
[pScanner scanHexLongLong:&firstHalf];
double hashBasedValueInRange0to1 = ((double)firstHalf) / 0xffffffffffffffff;

// Transform the number into a number between min and max
appDelaySeconds = ((RetryRangeSeconds * hashBasedValueInRange0to1) + MinRetrySeconds);
}

return appDelaySeconds;
}

#pragma mark Create Sessions

/**
Expand Down
39 changes: 30 additions & 9 deletions SmartDeviceLink/private/SDLLifecycleProtocolHandler.m
Expand Up @@ -26,16 +26,19 @@
#import "SDLTimer.h"

static const float StartSessionTime = 10.0;
int const RPCStartServiceRetries = 2;

NS_ASSUME_NONNULL_BEGIN

@interface SDLLifecycleProtocolHandler ()

@property (weak, nonatomic) SDLNotificationDispatcher *notificationDispatcher;

@property (strong, nonatomic) SDLTimer *rpcStartServiceTimeoutTimer;
@property (strong, nonatomic, nullable) SDLTimer *rpcStartServiceTimeoutTimer;
@property (assign, nonatomic) int rpcStartServiceRetryCounter;
@property (copy, nonatomic) NSString *appId;


@end

@implementation SDLLifecycleProtocolHandler
Expand All @@ -44,6 +47,7 @@ - (instancetype)initWithProtocol:(SDLProtocol *)protocol notificationDispatcher:
self = [super init];
if (!self) { return nil; }

_rpcStartServiceRetryCounter = 0;
_protocol = protocol;
_notificationDispatcher = notificationDispatcher;

Expand Down Expand Up @@ -72,20 +76,37 @@ - (void)stopWithCompletionHandler:(void (^)(void))disconnectCompletionHandler {
- (void)protocolDidOpen:(SDLProtocol *)protocol {
if (self.protocol != protocol) { return; }

SDLLogD(@"Transport opened, sending an RPC Start Service, and starting timer for RPC Start Service ACK to be received.");
SDLLogD(@"Transport opened.");
[self.notificationDispatcher postNotificationName:SDLTransportDidConnect infoObject:nil];

[self sdl_sendStartServiceSession:self.rpcStartServiceRetryCounter];
}

/// Sends the RPC start service request to the module and sets a timer to wait for a response from the module. If the module ACKs or NAKs the request, then the timer is canceled. However, if the module does not respond within a set amount of time, the RPC start service is resent. Once the max number of retry accounts has been reached the session is closed.
/// @param retryCount The retry attempt count
- (void)sdl_sendStartServiceSession:(int)retryCount {
if (retryCount > RPCStartServiceRetries) {
SDLLogE(@"Retrying sending the RPC start service failed %d times. Closing the session", RPCStartServiceRetries);
return [self.protocol stopWithCompletionHandler:^{}];
}

SDLControlFramePayloadRPCStartService *startServicePayload = [[SDLControlFramePayloadRPCStartService alloc] initWithVersion:SDLMaxProxyProtocolVersion];
[self.protocol startServiceWithType:SDLServiceTypeRPC payload:startServicePayload.data];

if (self.rpcStartServiceTimeoutTimer == nil) {
self.rpcStartServiceTimeoutTimer = [[SDLTimer alloc] initWithDuration:StartSessionTime repeat:NO];
__weak typeof(self) weakSelf = self;
self.rpcStartServiceTimeoutTimer.elapsedBlock = ^{
SDLLogE(@"Start session timed out after %f seconds, closing the connection.", StartSessionTime);
[weakSelf.protocol stopWithCompletionHandler:^{}];
};
if (self.rpcStartServiceTimeoutTimer != nil) {
[self.rpcStartServiceTimeoutTimer cancel];
self.rpcStartServiceTimeoutTimer = nil;
}

self.rpcStartServiceTimeoutTimer = [[SDLTimer alloc] initWithDuration:StartSessionTime repeat:NO];
__weak typeof(self) weakSelf = self;
self.rpcStartServiceTimeoutTimer.elapsedBlock = ^{
weakSelf.rpcStartServiceRetryCounter += 1;
SDLLogE(@"Module did not respond to the RPC start service within %.f seconds. Retrying sending the RPC start service (#%d)", StartSessionTime, weakSelf.rpcStartServiceRetryCounter);
[weakSelf sdl_sendStartServiceSession:weakSelf.rpcStartServiceRetryCounter];
};

SDLLogD(@"Starting timeout timer for the module's response to the RPC start service");
[self.rpcStartServiceTimeoutTimer start];
}

Expand Down