summaryrefslogtreecommitdiff
path: root/SmartDeviceLink/SDLLifecycleManager.m
diff options
context:
space:
mode:
Diffstat (limited to 'SmartDeviceLink/SDLLifecycleManager.m')
-rw-r--r--SmartDeviceLink/SDLLifecycleManager.m182
1 files changed, 126 insertions, 56 deletions
diff --git a/SmartDeviceLink/SDLLifecycleManager.m b/SmartDeviceLink/SDLLifecycleManager.m
index 481ceb9ff..1323c3cd1 100644
--- a/SmartDeviceLink/SDLLifecycleManager.m
+++ b/SmartDeviceLink/SDLLifecycleManager.m
@@ -12,6 +12,7 @@
#import "NSMapTable+Subscripting.h"
#import "SDLAbstractProtocol.h"
+#import "SDLAsynchronousRPCRequestOperation.h"
#import "SDLConfiguration.h"
#import "SDLConnectionManagerType.h"
#import "SDLLogMacros.h"
@@ -39,6 +40,7 @@
#import "SDLRegisterAppInterfaceResponse.h"
#import "SDLResponseDispatcher.h"
#import "SDLResult.h"
+#import "SDLSequentialRPCRequestOperation.h"
#import "SDLSetAppIcon.h"
#import "SDLStateMachine.h"
#import "SDLStreamingMediaConfiguration.h"
@@ -74,6 +76,7 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
// Private properties
@property (copy, nonatomic) SDLManagerReadyBlock readyHandler;
+@property (copy, nonatomic) dispatch_queue_t lifecycleQueue;
@end
@@ -108,6 +111,11 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
_responseDispatcher = [[SDLResponseDispatcher alloc] initWithNotificationDispatcher:_notificationDispatcher];
_registerResponse = nil;
+ _rpcOperationQueue = [[NSOperationQueue alloc] init];
+ _rpcOperationQueue.name = @"com.sdl.lifecycle.rpcOperation.concurrent";
+ _rpcOperationQueue.maxConcurrentOperationCount = 3;
+ _lifecycleQueue = dispatch_queue_create("com.sdl.lifecycle", DISPATCH_QUEUE_SERIAL);
+
// Managers
_fileManager = [[SDLFileManager alloc] initWithConnectionManager:self];
_permissionManager = [[SDLPermissionManager alloc] init];
@@ -132,6 +140,12 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
}
- (void)startWithReadyHandler:(SDLManagerReadyBlock)readyHandler {
+ dispatch_sync(_lifecycleQueue, ^{
+ [self sdl_startWithReadyHandler:readyHandler];
+ });
+}
+
+- (void)sdl_startWithReadyHandler:(SDLManagerReadyBlock)readyHandler {
if (![self.lifecycleStateMachine isCurrentState:SDLLifecycleStateStopped]) {
SDLLogW(@"Warning: SDL has already been started, this attempt will be ignored");
return;
@@ -144,12 +158,14 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
}
- (void)stop {
- SDLLogD(@"Lifecycle manager stopped");
- if ([self.lifecycleStateMachine isCurrentState:SDLLifecycleStateReady]) {
- [self.lifecycleStateMachine transitionToState:SDLLifecycleStateUnregistering];
- } else {
- [self.lifecycleStateMachine transitionToState:SDLLifecycleStateStopped];
- }
+ dispatch_sync(_lifecycleQueue, ^{
+ SDLLogD(@"Lifecycle manager stopped");
+ if ([self.lifecycleStateMachine isCurrentState:SDLLifecycleStateReady]) {
+ [self.lifecycleStateMachine transitionToState:SDLLifecycleStateUnregistering];
+ } else {
+ [self.lifecycleStateMachine transitionToState:SDLLifecycleStateStopped];
+ }
+ });
}
@@ -211,6 +227,8 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
[self.streamManager stop];
[self.responseDispatcher clear];
+ [self.rpcOperationQueue cancelAllOperations];
+
self.registerResponse = nil;
self.lastCorrelationId = 0;
self.hmiLevel = nil;
@@ -244,20 +262,22 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
__weak typeof(self) weakSelf = self;
[self sdl_sendRequest:regRequest
withResponseHandler:^(__kindof SDLRPCRequest *_Nullable request, __kindof SDLRPCResponse *_Nullable response, NSError *_Nullable error) {
- // If the success BOOL is NO or we received an error at this point, we failed. Call the ready handler and transition to the DISCONNECTED state.
- if (error != nil || ![response.success boolValue]) {
- SDLLogE(@"Failed to register the app. Error: %@, Response: %@", error, response);
- weakSelf.readyHandler(NO, error);
+ dispatch_async(weakSelf.lifecycleQueue, ^{
+ // If the success BOOL is NO or we received an error at this point, we failed. Call the ready handler and transition to the DISCONNECTED state.
+ if (error != nil || ![response.success boolValue]) {
+ SDLLogE(@"Failed to register the app. Error: %@, Response: %@", error, response);
+ weakSelf.readyHandler(NO, error);
+
+ if (weakSelf.lifecycleState != SDLLifecycleStateReconnecting) {
+ [weakSelf.lifecycleStateMachine transitionToState:SDLLifecycleStateStopped];
+ }
- if (weakSelf.lifecycleState != SDLLifecycleStateReconnecting) {
- [weakSelf.lifecycleStateMachine transitionToState:SDLLifecycleStateStopped];
+ return;
}
-
- return;
- }
- weakSelf.registerResponse = (SDLRegisterAppInterfaceResponse *)response;
- [weakSelf.lifecycleStateMachine transitionToState:SDLLifecycleStateRegistered];
+ weakSelf.registerResponse = (SDLRegisterAppInterfaceResponse *)response;
+ [weakSelf.lifecycleStateMachine transitionToState:SDLLifecycleStateRegistered];
+ });
}];
}
@@ -338,18 +358,21 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
dispatch_group_leave(managerGroup);
// When done, we want to transition, even if there were errors. They may be expected, e.g. on head units that do not support files.
- dispatch_group_notify(managerGroup, dispatch_get_main_queue(), ^{
+ dispatch_group_notify(managerGroup, self.lifecycleQueue, ^{
[self.lifecycleStateMachine transitionToState:SDLLifecycleStateSettingUpAppIcon];
});
}
- (void)didEnterStateSettingUpAppIcon {
// We only want to send the app icon when the file manager is complete, and when that's done, wait for hmi status to be ready
+ __weak typeof(self) weakself = self;
[self sdl_sendAppIcon:self.configuration.lifecycleConfig.appIcon withCompletion:^() {
- // We could have been shut down while setting up the app icon, make sure we still want to continue or we could crash
- if (self.lifecycleState == SDLLifecycleStateSettingUpAppIcon) {
- [self.lifecycleStateMachine transitionToState:SDLLifecycleStateSettingUpHMI];
- }
+ dispatch_async(weakself.lifecycleQueue, ^{
+ // We could have been shut down while setting up the app icon, make sure we still want to continue or we could crash
+ if (weakself.lifecycleState == SDLLifecycleStateSettingUpAppIcon) {
+ [weakself.lifecycleStateMachine transitionToState:SDLLifecycleStateSettingUpHMI];
+ }
+ });
}];
}
@@ -375,16 +398,21 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
}
// If we got to this point, we succeeded, send the error if there was a warning.
- self.readyHandler(YES, startError);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ self.readyHandler(YES, startError);
+ });
+
[self.notificationDispatcher postNotificationName:SDLDidBecomeReady infoObject:nil];
// Send the hmi level going from NONE to whatever we're at now (could still be NONE)
- [self.delegate hmiLevel:SDLHMILevelNone didChangeToLevel:self.hmiLevel];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self.delegate hmiLevel:SDLHMILevelNone didChangeToLevel:self.hmiLevel];
- // Send the audio streaming state going from NOT_AUDIBLE to whatever we're at now (could still be NOT_AUDIBLE)
- if ([self.delegate respondsToSelector:@selector(audioStreamingState:didChangeToState:)]) {
- [self.delegate audioStreamingState:SDLAudioStreamingStateNotAudible didChangeToState:self.audioStreamingState];
- }
+ // Send the audio streaming state going from NOT_AUDIBLE to whatever we're at now (could still be NOT_AUDIBLE)
+ if ([self.delegate respondsToSelector:@selector(audioStreamingState:didChangeToState:)]) {
+ [self.delegate audioStreamingState:SDLAudioStreamingStateNotAudible didChangeToState:self.audioStreamingState];
+ }
+ });
}
- (void)didEnterStateUnregistering {
@@ -447,25 +475,46 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
}
- (void)sendRequest:(__kindof SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler {
+ SDLAsynchronousRPCRequestOperation *op = [[SDLAsynchronousRPCRequestOperation alloc] initWithConnectionManager:self request:request responseHandler:handler];
+ [self.rpcOperationQueue addOperation:op];
+}
+
+- (void)sendRequests:(NSArray<SDLRPCRequest *> *)requests progressHandler:(nullable SDLMultipleAsyncRequestProgressHandler)progressHandler completionHandler:(nullable SDLMultipleRequestCompletionHandler)completionHandler {
+ SDLAsynchronousRPCRequestOperation *op = [[SDLAsynchronousRPCRequestOperation alloc] initWithConnectionManager:self requests:requests progressHandler:progressHandler completionHandler:completionHandler];
+ [self.rpcOperationQueue addOperation:op];
+}
+
+- (void)sendSequentialRequests:(NSArray<SDLRPCRequest *> *)requests progressHandler:(nullable SDLMultipleSequentialRequestProgressHandler)progressHandler completionHandler:(nullable SDLMultipleRequestCompletionHandler)completionHandler {
+ SDLSequentialRPCRequestOperation *op = [[SDLSequentialRPCRequestOperation alloc] initWithConnectionManager:self requests:requests progressHandler:progressHandler completionHandler:completionHandler];
+ [self.rpcOperationQueue addOperation:op];
+}
+
+- (void)sendConnectionRequest:(__kindof SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler {
if (![self.lifecycleStateMachine isCurrentState:SDLLifecycleStateReady]) {
SDLLogW(@"Manager not ready, message not sent (%@)", request);
if (handler) {
- handler(request, nil, [NSError sdl_lifecycle_notReadyError]);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ handler(request, nil, [NSError sdl_lifecycle_notReadyError]);
+ });
}
return;
}
- [self sdl_sendRequest:request withResponseHandler:handler];
+ dispatch_async(_lifecycleQueue, ^{
+ [self sdl_sendRequest:request withResponseHandler:handler];
+ });
}
// Managers need to avoid state checking. Part of <SDLConnectionManagerType>.
-- (void)sendManagerRequest:(__kindof SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)block {
- [self sdl_sendRequest:request withResponseHandler:block];
+- (void)sendConnectionManagerRequest:(__kindof SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler {
+ dispatch_async(_lifecycleQueue, ^{
+ [self sdl_sendRequest:request withResponseHandler:handler];
+ });
}
- (void)sdl_sendRequest:(SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler {
- // We will allow things to be sent in a "SDLLifeCycleStateConnected" state in the private method, but block it in the public method sendRequest:withCompletionHandler: so that the lifecycle manager can complete its setup without being bothered by developer error
+ // We will allow things to be sent in a "SDLLifecycleStateConnected" state in the private method, but block it in the public method sendRequest:withCompletionHandler: so that the lifecycle manager can complete its setup without being bothered by developer error
NSParameterAssert(request != nil);
// If, for some reason, the request is nil we should error out.
@@ -473,7 +522,9 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
NSError *error = [NSError sdl_lifecycle_rpcErrorWithDescription:@"Nil Request Sent" andReason:@"A nil RPC request was passed and cannot be sent."];
SDLLogW(@"%@", error);
if (handler) {
- handler(nil, nil, error);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ handler(nil, nil, error);
+ });
}
return;
}
@@ -510,20 +561,31 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
- (void)transportDidConnect {
SDLLogD(@"Transport connected");
- [self.lifecycleStateMachine transitionToState:SDLLifecycleStateConnected];
+
+ dispatch_async(self.lifecycleQueue, ^{
+ [self.lifecycleStateMachine transitionToState:SDLLifecycleStateConnected];
+ });
}
- (void)transportDidDisconnect {
SDLLogD(@"Transport Disconnected");
- if (self.lifecycleState == SDLLifecycleStateUnregistering || self.lifecycleState == SDLLifecycleStateStopped) {
- [self.lifecycleStateMachine transitionToState:SDLLifecycleStateStopped];
- } else {
- [self.lifecycleStateMachine transitionToState:SDLLifecycleStateReconnecting];
- }
+ dispatch_async(self.lifecycleQueue, ^{
+ if (self.lifecycleState == SDLLifecycleStateUnregistering || self.lifecycleState == SDLLifecycleStateStopped) {
+ [self.lifecycleStateMachine transitionToState:SDLLifecycleStateStopped];
+ } else {
+ [self.lifecycleStateMachine transitionToState:SDLLifecycleStateReconnecting];
+ }
+ });
}
- (void)hmiStatusDidChange:(SDLRPCNotificationNotification *)notification {
+ dispatch_async(self.lifecycleQueue, ^{
+ [self sdl_hmiStatusDidChange:notification];
+ });
+}
+
+- (void)sdl_hmiStatusDidChange:(SDLRPCNotificationNotification *)notification {
if (![notification isNotificationMemberOfClass:[SDLOnHMIStatus class]]) {
return;
}
@@ -531,17 +593,17 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
SDLOnHMIStatus *hmiStatusNotification = notification.notification;
SDLHMILevel oldHMILevel = self.hmiLevel;
self.hmiLevel = hmiStatusNotification.hmiLevel;
-
+
SDLAudioStreamingState oldStreamingState = self.audioStreamingState;
self.audioStreamingState = hmiStatusNotification.audioStreamingState;
-
+
SDLSystemContext oldSystemContext = self.systemContext;
self.systemContext = hmiStatusNotification.systemContext;
SDLLogD(@"HMI level changed from %@ to %@", oldHMILevel, self.hmiLevel);
SDLLogD(@"Audio streaming state changed from %@ to %@", oldStreamingState, self.audioStreamingState);
- if ([self.lifecycleStateMachine isCurrentState:SDLLifecycleStateSettingUpHMI]) {
+ if ([self.lifecycleStateMachine isCurrentState:SDLLifecycleStateSettingUpHMI]) {
[self.lifecycleStateMachine transitionToState:SDLLifecycleStateReady];
}
@@ -549,22 +611,30 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
return;
}
- if (![oldHMILevel isEqualToEnum:self.hmiLevel]) {
- [self.delegate hmiLevel:oldHMILevel didChangeToLevel:self.hmiLevel];
- }
-
- if (![oldStreamingState isEqualToEnum:self.audioStreamingState]
- && [self.delegate respondsToSelector:@selector(audioStreamingState:didChangeToState:)]) {
- [self.delegate audioStreamingState:oldStreamingState didChangeToState:self.audioStreamingState];
- }
-
- if (![oldSystemContext isEqualToEnum:self.systemContext]
- && [self.delegate respondsToSelector:@selector(systemContext:didChangeToContext:)]) {
- [self.delegate systemContext:oldSystemContext didChangeToContext:self.systemContext];
- }
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (![oldHMILevel isEqualToEnum:self.hmiLevel]) {
+ [self.delegate hmiLevel:oldHMILevel didChangeToLevel:self.hmiLevel];
+ }
+
+ if (![oldStreamingState isEqualToEnum:self.audioStreamingState]
+ && [self.delegate respondsToSelector:@selector(audioStreamingState:didChangeToState:)]) {
+ [self.delegate audioStreamingState:oldStreamingState didChangeToState:self.audioStreamingState];
+ }
+
+ if (![oldSystemContext isEqualToEnum:self.systemContext]
+ && [self.delegate respondsToSelector:@selector(systemContext:didChangeToContext:)]) {
+ [self.delegate systemContext:oldSystemContext didChangeToContext:self.systemContext];
+ }
+ });
}
- (void)remoteHardwareDidUnregister:(SDLRPCNotificationNotification *)notification {
+ dispatch_async(self.lifecycleQueue, ^{
+ [self sdl_remoteHardwareDidUnregister:notification];
+ });
+}
+
+- (void)sdl_remoteHardwareDidUnregister:(SDLRPCNotificationNotification *)notification {
if (![notification isNotificationMemberOfClass:[SDLOnAppInterfaceUnregistered class]]) {
return;
}