diff options
author | NicoleYarroch <nicole@livio.io> | 2020-07-13 11:07:04 -0400 |
---|---|---|
committer | NicoleYarroch <nicole@livio.io> | 2020-07-13 11:07:04 -0400 |
commit | 203f2f26bfc1c93ee00587283f8d703188d1280d (patch) | |
tree | 85f88b7685abfce49ca4604ec4f7c8bd89fb6c1f | |
parent | 62fc5f841c3b3a88164196b2f34bb235120d3bf1 (diff) | |
download | sdl_ios-203f2f26bfc1c93ee00587283f8d703188d1280d.tar.gz |
Serialized access to capabilityObservers
Signed-off-by: NicoleYarroch <nicole@livio.io>
-rw-r--r-- | SmartDeviceLink/SDLSystemCapabilityManager.m | 214 |
1 files changed, 124 insertions, 90 deletions
diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index aed6516f8..54aca24a5 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -78,6 +78,8 @@ typedef NSString * SDLServiceID; @property (assign, nonatomic) BOOL shouldConvertDeprecatedDisplayCapabilities; @property (strong, nonatomic) SDLHMILevel currentHMILevel; +@property (copy, nonatomic) dispatch_queue_t readWriteQueue; + @end @implementation SDLSystemCapabilityManager @@ -90,6 +92,12 @@ typedef NSString * SDLServiceID; return nil; } + if (@available(iOS 10.0, *)) { + _readWriteQueue = dispatch_queue_create_with_target("com.sdl.lifecycle.responseDispatcher", DISPATCH_QUEUE_SERIAL, [SDLGlobals sharedGlobals].sdlProcessingQueue); + } else { + _readWriteQueue = [SDLGlobals sharedGlobals].sdlProcessingQueue; + } + _connectionManager = manager; _shouldConvertDeprecatedDisplayCapabilities = YES; _appServicesCapabilitiesDictionary = [NSMutableDictionary dictionary]; @@ -128,14 +136,15 @@ typedef NSString * SDLServiceID; _videoStreamingCapability = nil; _remoteControlCapability = nil; _seatLocationCapability = nil; - _appServicesCapabilitiesDictionary = [NSMutableDictionary dictionary]; _supportsSubscriptions = NO; - [_capabilityObservers removeAllObjects]; - [_subscriptionStatus removeAllObjects]; + [self sdl_runSyncOnQueue:^{ + self->_appServicesCapabilitiesDictionary = [NSMutableDictionary dictionary]; + [self->_capabilityObservers removeAllObjects]; + [self->_subscriptionStatus removeAllObjects]; + }]; _currentHMILevel = SDLHMILevelNone; - _shouldConvertDeprecatedDisplayCapabilities = YES; } @@ -510,64 +519,22 @@ typedef NSString * SDLServiceID; #pragma mark - Manager Subscriptions +#pragma mark Subscribing + - (nullable id<NSObject>)subscribeToCapabilityType:(SDLSystemCapabilityType)type withBlock:(SDLCapabilityUpdateHandler)block { SDLLogD(@"Subscribing to capability type: %@ with a handler (DEPRECATED)", type); SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:[[NSObject alloc] init] block:block]; - if ([self.currentHMILevel isEqualToEnum:SDLHMILevelNone] && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { - SDLLogE(@"Attempted to subscribe to type: %@ in HMI level NONE, which is not allowed. Please wait until you are in HMI BACKGROUND, LIMITED, or FULL before attempting to subscribe to any SystemCapabilityType other than DISPLAYS.", type); - [self sdl_invokeObserver:observerObject withCapabilityType:type capability:nil error:[NSError sdl_systemCapabilityManager_cannotUpdateInHMINONE]]; - return nil; - } - - if (self.capabilityObservers[type] == nil) { - SDLLogD(@"This is the first subscription to capability type: %@, sending a GetSystemCapability with subscribe true", type); - self.capabilityObservers[type] = [NSMutableArray arrayWithObject:observerObject]; - - // We don't want to send this for the displays type because that's automatically subscribed - if (![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { - [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:nil]; - } else { - // If we're not calling the GSC RPC we should invoke the observer with the cached data - [self sdl_invokeObserver:observerObject withCapabilityType:type capability:[self sdl_cachedCapabilityForType:type] error:nil]; - } - } else { - // Store the observer and call it immediately with the cached value - [self.capabilityObservers[type] addObject:observerObject]; - [self sdl_invokeObserver:observerObject withCapabilityType:type capability:[self sdl_cachedCapabilityForType:type] error:nil]; - } - - return observerObject.observer; + id<NSObject> subscribedObserver = [self sdl_subscribeToCapabilityType:type observerObject:observerObject]; + return subscribedObserver; } - (nullable id<NSObject>)subscribeToCapabilityType:(SDLSystemCapabilityType)type withUpdateHandler:(SDLCapabilityUpdateWithErrorHandler)handler { SDLLogD(@"Subscribing to capability type: %@ with a handler", type); SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:[[NSObject alloc] init] updateHandler:handler]; - if ([self.currentHMILevel isEqualToEnum:SDLHMILevelNone] && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { - SDLLogE(@"Attempted to subscribe to type: %@ in HMI level NONE, which is not allowed. Please wait until you are in HMI BACKGROUND, LIMITED, or FULL before attempting to subscribe to any SystemCapabilityType other than DISPLAYS.", type); - [self sdl_invokeObserver:observerObject withCapabilityType:type capability:nil error:[NSError sdl_systemCapabilityManager_cannotUpdateInHMINONE]]; - return nil; - } - - if (self.capabilityObservers[type] == nil) { - SDLLogD(@"This is the first subscription to capability type: %@, sending a GetSystemCapability with subscribe true", type); - self.capabilityObservers[type] = [NSMutableArray arrayWithObject:observerObject]; - - // We don't want to send this for the displays type because that's automatically subscribed - if (![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { - [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:nil]; - } else { - // If we're not calling the GSC RPC we should invoke the observer with the cached data - [self sdl_invokeObserver:observerObject withCapabilityType:type capability:[self sdl_cachedCapabilityForType:type] error:nil]; - } - } else { - // Store the observer and call it immediately with the cached value - [self.capabilityObservers[type] addObject:observerObject]; - [self sdl_invokeObserver:observerObject withCapabilityType:type capability:[self sdl_cachedCapabilityForType:type] error:nil]; - } - - return observerObject.observer; + id<NSObject> subscribedObserver = [self sdl_subscribeToCapabilityType:type observerObject:observerObject]; + return subscribedObserver; } - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id<NSObject>)observer selector:(SEL)selector { @@ -584,15 +551,27 @@ typedef NSString * SDLServiceID; } SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:observer selector:selector]; + + id<NSObject> subscribedObserver = [self sdl_subscribeToCapabilityType:type observerObject:observerObject]; + return subscribedObserver == nil ? NO : YES; +} + +/// Helper method for subscribing to a system capability type +/// @param type The SystemCapabilityType that will be subscribed +/// @param observerObject An object that can be used to unsubscribe the block. If nil, the subscription was not succesful. +- (nullable id<NSObject>)sdl_subscribeToCapabilityType:(SDLSystemCapabilityType)type observerObject:(SDLSystemCapabilityObserver *)observerObject { if ([self.currentHMILevel isEqualToEnum:SDLHMILevelNone] && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { SDLLogE(@"Attempted to subscribe to type: %@ in HMI level NONE, which is not allowed. Please wait until you are in HMI BACKGROUND, LIMITED, or FULL before attempting to subscribe to any SystemCapabilityType other than DISPLAYS.", type); [self sdl_invokeObserver:observerObject withCapabilityType:type capability:nil error:[NSError sdl_systemCapabilityManager_cannotUpdateInHMINONE]]; - return NO; + return nil; } if (self.capabilityObservers[type] == nil) { SDLLogD(@"This is the first subscription to capability type: %@, sending a GetSystemCapability with subscribe true", type); - self.capabilityObservers[type] = [NSMutableArray arrayWithObject:observerObject]; + + [self sdl_runSyncOnQueue:^{ + self.capabilityObservers[type] = [NSMutableArray arrayWithObject:observerObject]; + }]; // We don't want to send this for the displays type because that's automatically subscribed if (![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { @@ -603,18 +582,25 @@ typedef NSString * SDLServiceID; } } else { // Store the observer and call it immediately with the cached value - [self.capabilityObservers[type] addObject:observerObject]; + [self sdl_runSyncOnQueue:^{ + [self.capabilityObservers[type] addObject:observerObject]; + }]; + [self sdl_invokeObserver:observerObject withCapabilityType:type capability:[self sdl_cachedCapabilityForType:type] error:nil]; } - return YES; + return observerObject.observer; } +#pragma mark Unubscribing + - (void)unsubscribeFromCapabilityType:(SDLSystemCapabilityType)type withObserver:(id)observer { SDLLogD(@"Unsubscribing from capability type: %@", type); for (SDLSystemCapabilityObserver *capabilityObserver in self.capabilityObservers[type]) { if ([observer isEqual:capabilityObserver.observer] && self.capabilityObservers[type] != nil) { - [self.capabilityObservers[type] removeObject:capabilityObserver]; + [self sdl_runSyncOnQueue:^{ + [self.capabilityObservers[type] removeObject:capabilityObserver]; + }]; [self sdl_removeNilObserversAndUnsubscribeIfNecessary]; break; @@ -622,6 +608,45 @@ typedef NSString * SDLServiceID; } } +- (void)sdl_removeNilObserversAndUnsubscribeIfNecessary { + SDLLogV(@"Checking for nil observers and removing them, then checking for subscriptions we don't need and unsubscribing."); + // Loop through our observers + for (SDLSystemCapabilityType key in self.capabilityObservers.allKeys) { + for (SDLSystemCapabilityObserver *observer in self.capabilityObservers[key]) { + // If an observer object is nil, remove it + if (observer.observer == nil) { + [self sdl_runSyncOnQueue:^{ + [self.capabilityObservers[key] removeObject:observer]; + }]; + } + + // If we no longer have any observers for that type, remove the array + if (self.capabilityObservers[key].count == 0) { + [self sdl_runSyncOnQueue:^{ + [self.capabilityObservers removeObjectForKey:key]; + }]; + } + } + } + + // If we don't support subscriptions, we don't want to unsubscribe by sending an RPC below + if (!self.supportsSubscriptions) { + return; + } + + // Loop through our subscription statuses, check if we're subscribed. If we are, and we do not have observers for that type, and that type is not DISPLAYS, then unsubscribe. + for (SDLSystemCapabilityType type in self.subscriptionStatus.allKeys) { + if ([self.subscriptionStatus[type] isEqualToNumber:@YES] + && self.capabilityObservers[type] == nil + && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { + SDLLogD(@"Removing the last subscription to type %@, sending a GetSystemCapability with subscribe false (will unsubscribe)", type); + [self sdl_sendGetSystemCapabilityWithType:type subscribe:@NO completionHandler:nil]; + } + } +} + +#pragma mark Notifying Subscribers + /// Calls all observers of a capability type with an updated capability /// @param capability The new capability update /// @param handler The update handler to call, if one exists after the observers are called @@ -676,39 +701,6 @@ typedef NSString * SDLServiceID; } } -- (void)sdl_removeNilObserversAndUnsubscribeIfNecessary { - SDLLogV(@"Checking for nil observers and removing them, then checking for subscriptions we don't need and unsubscribing."); - // Loop through our observers - for (SDLSystemCapabilityType key in self.capabilityObservers.allKeys) { - for (SDLSystemCapabilityObserver *observer in self.capabilityObservers[key]) { - // If an observer object is nil, remove it - if (observer.observer == nil) { - [self.capabilityObservers[key] removeObject:observer]; - } - - // If we no longer have any observers for that type, remove the array - if (self.capabilityObservers[key].count == 0) { - [self.capabilityObservers removeObjectForKey:key]; - } - } - } - - // If we don't support subscriptions, we don't want to unsubscribe by sending an RPC below - if (!self.supportsSubscriptions) { - return; - } - - // Loop through our subscription statuses, check if we're subscribed. If we are, and we do not have observers for that type, and that type is not DISPLAYS, then unsubscribe. - for (SDLSystemCapabilityType type in self.subscriptionStatus.allKeys) { - if ([self.subscriptionStatus[type] isEqualToNumber:@YES] - && self.capabilityObservers[type] == nil - && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { - SDLLogD(@"Removing the last subscription to type %@, sending a GetSystemCapability with subscribe false (will unsubscribe)", type); - [self sdl_sendGetSystemCapabilityWithType:type subscribe:@NO completionHandler:nil]; - } - } -} - #pragma mark - Notifications /// Registers for notifications and responses from Core @@ -801,6 +793,48 @@ typedef NSString * SDLServiceID; self.currentHMILevel = onHMIStatus.hmiLevel; } +#pragma mark Utilities + +/// Checks if we are already on the serial readWrite queue. If so, the block is added to the queue; if not, the block is dispatched to the readWrite queue. +/// @discussion Used to ensure atomic access to global properties. +/// @param block The block to be executed. +- (void)sdl_runSyncOnQueue:(void (^)(void))block { + if (dispatch_get_specific(SDLProcessingQueueName) != nil) { + block(); + } else { + dispatch_sync(self.readWriteQueue, block); + } +} + +#pragma mark Getters + +- (NSMutableDictionary<SDLSystemCapabilityType, NSMutableArray<SDLSystemCapabilityObserver *> *> *)capabilityObservers { + __block NSMutableDictionary<SDLSystemCapabilityType, NSMutableArray<SDLSystemCapabilityObserver *> *> *dict = nil; + [self sdl_runSyncOnQueue:^{ + dict = self->_capabilityObservers; + }]; + + return dict; +} + +- (NSMutableDictionary<SDLSystemCapabilityType,NSNumber<SDLBool> *> *)subscriptionStatus { + __block NSMutableDictionary<SDLSystemCapabilityType,NSNumber<SDLBool> *> *dict = nil; + [self sdl_runSyncOnQueue:^{ + dict = self->_subscriptionStatus; + }]; + + return dict; +} + +- (nullable NSMutableDictionary<SDLServiceID,SDLAppServiceCapability *> *)appServicesCapabilitiesDictionary { + __block NSMutableDictionary<SDLServiceID,SDLAppServiceCapability *> *dict = nil; + [self sdl_runSyncOnQueue:^{ + dict = self->_appServicesCapabilitiesDictionary; + }]; + + return dict; +} + @end NS_ASSUME_NONNULL_END |