From b635c0aaf80bc0b94fbacc2c48bb5b260e0d47d7 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 29 Jan 2020 16:33:11 -0500 Subject: [PATCH 01/39] Working on isCapabilitySupported --- SmartDeviceLink/SDLSystemCapabilityManager.h | 16 +- SmartDeviceLink/SDLSystemCapabilityManager.m | 258 +++++++++++-------- 2 files changed, 159 insertions(+), 115 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.h b/SmartDeviceLink/SDLSystemCapabilityManager.h index f5695b3bf..8d76e8551 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.h +++ b/SmartDeviceLink/SDLSystemCapabilityManager.h @@ -50,6 +50,14 @@ typedef void (^SDLUpdateCapabilityHandler)(NSError * _Nullable error, SDLSystemC */ typedef void (^SDLCapabilityUpdateHandler)(SDLSystemCapability *capability); +/** + An observer block for whenever a subscription or value is retrieved. + + @param capability The capability that was updated. + @param error An error that occurred. + */ +typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability *capability, NSError *error); + /** A manager that handles updating and subscribing to SDL capabilities. */ @@ -246,6 +254,11 @@ typedef void (^SDLCapabilityUpdateHandler)(SDLSystemCapability *capability); */ - (void)updateCapabilityType:(SDLSystemCapabilityType)type completionHandler:(SDLUpdateCapabilityHandler)handler; +/// Returns whether or not the capability type is supported on the system. You can use this to check if subscribing to the capability will work. +/// @param type The SystemCapabilityType that will be checked. +/// @return Whether or not `type` is supported by the connected head unit. +- (BOOL)isCapabilitySupported:(SDLSystemCapabilityType)type; + /** Subscribe to a particular capability type using a block callback @@ -259,7 +272,8 @@ typedef void (^SDLCapabilityUpdateHandler)(SDLSystemCapability *capability); * Subscribe to a particular capability type with a selector callback. The selector supports the following parameters: * * 1. No parameters e.g. `- (void)phoneCapabilityUpdated;` - * 2. One `SDLSystemCapability *` parameter e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability` + * 2. One `SDLSystemCapability *` parameter, e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability` + * 3. Two parameters, one `SDLSystemCapability *` parameter, and one `NSError *` parameter, e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability error:(NSError *)error` * * This method will be called immediately with the current value and called every time the value is updated on RPC v5.1.0+ systems (`supportsSubscriptions == YES`). If this method is called on a sub-v5.1.0 system (`supportsSubscriptions == NO`), the method will return `NO` and the selector will never be called. * diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 0f1c4faf3..3f772c1b0 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -152,120 +152,6 @@ - (nullable SDLAppServicesCapabilities *)appServicesCapabilities { return [[SDLAppServicesCapabilities alloc] initWithAppServices:self.appServicesCapabilitiesDictionary.allValues]; } -#pragma mark - Notifications - -/** - * Registers for notifications and responses from Core - */ -- (void)sdl_registerForNotifications { - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_registerResponse:) name:SDLDidReceiveRegisterAppInterfaceResponse object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_displayLayoutResponse:) name:SDLDidReceiveSetDisplayLayoutResponse object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_systemCapabilityUpdatedNotification:) name:SDLDidReceiveSystemCapabilityUpdatedNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_systemCapabilityResponseNotification:) name:SDLDidReceiveGetSystemCapabilitiesResponse object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_hmiStatusNotification:) name:SDLDidChangeHMIStatusNotification object:nil]; -} - -/** - * Called when a `RegisterAppInterfaceResponse` response is received from Core. The head unit capabilities are saved. - * - * @param notification The `RegisterAppInterfaceResponse` response received from Core - */ -- (void)sdl_registerResponse:(SDLRPCResponseNotification *)notification { - SDLRegisterAppInterfaceResponse *response = (SDLRegisterAppInterfaceResponse *)notification.response; - if (!response.success.boolValue) { return; } - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated" - self.displayCapabilities = response.displayCapabilities; - self.softButtonCapabilities = response.softButtonCapabilities; - self.buttonCapabilities = response.buttonCapabilities; - self.presetBankCapabilities = response.presetBankCapabilities; -#pragma clang diagnostic pop - - self.hmiCapabilities = response.hmiCapabilities; - self.hmiZoneCapabilities = response.hmiZoneCapabilities; - self.speechCapabilities = response.speechCapabilities; - self.prerecordedSpeechCapabilities = response.prerecordedSpeech; - self.vrCapability = (response.vrCapabilities.count > 0 && [response.vrCapabilities.firstObject isEqualToEnum:SDLVRCapabilitiesText]) ? YES : NO; - self.audioPassThruCapabilities = response.audioPassThruCapabilities; - self.pcmStreamCapability = response.pcmStreamCapabilities; - - self.shouldConvertDeprecatedDisplayCapabilities = YES; - self.displays = [self sdl_createDisplayCapabilityListFromRegisterResponse:response]; - - // call the observers in case the new display capability list is created from deprecated types - SDLSystemCapability *systemCapability = [[SDLSystemCapability alloc] initWithDisplayCapabilities:self.displays]; - [self sdl_callObserversForCapabilityUpdate:systemCapability handler:nil]; -} - -/** - * Called when a `SetDisplayLayoutResponse` response is received from Core. If the template was set successfully, the the new capabilities for the template are saved. - * - * @param notification The `SetDisplayLayoutResponse` response received from Core - */ -- (void)sdl_displayLayoutResponse:(SDLRPCResponseNotification *)notification { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated" - SDLSetDisplayLayoutResponse *response = (SDLSetDisplayLayoutResponse *)notification.response; -#pragma clang diagnostic pop - if (!response.success.boolValue) { return; } - - // If we've received a display capability update then we should not convert our deprecated display capabilities and we should just return - if (!self.shouldConvertDeprecatedDisplayCapabilities) { return; } - - self.displayCapabilities = response.displayCapabilities; - self.buttonCapabilities = response.buttonCapabilities; - self.softButtonCapabilities = response.softButtonCapabilities; - self.presetBankCapabilities = response.presetBankCapabilities; - - self.displays = [self sdl_createDisplayCapabilityListFromSetDisplayLayoutResponse:response]; - - // Call the observers in case the new display capability list is created from deprecated types - SDLSystemCapability *systemCapability = [[SDLSystemCapability alloc] initWithDisplayCapabilities:self.displays]; - [self sdl_callObserversForCapabilityUpdate:systemCapability handler:nil]; -} - - -/** - * Called when an `OnSystemCapabilityUpdated` notification is received from Core. The updated system capabilty is saved. - * - * @param notification The `OnSystemCapabilityUpdated` notification received from Core - */ -- (void)sdl_systemCapabilityUpdatedNotification:(SDLRPCNotificationNotification *)notification { - SDLOnSystemCapabilityUpdated *systemCapabilityUpdatedNotification = (SDLOnSystemCapabilityUpdated *)notification.notification; - [self sdl_saveSystemCapability:systemCapabilityUpdatedNotification.systemCapability completionHandler:nil]; -} - -/** - Called with a `GetSystemCapabilityResponse` notification is received from core. The updated system capability is saved. - - @param notification The `GetSystemCapabilityResponse` notification received from Core - */ -- (void)sdl_systemCapabilityResponseNotification:(SDLRPCResponseNotification *)notification { - SDLGetSystemCapabilityResponse *systemCapabilityResponse = (SDLGetSystemCapabilityResponse *)notification.response; - [self sdl_saveSystemCapability:systemCapabilityResponse.systemCapability completionHandler:nil]; -} - -/** - * Called when an `OnHMIStatus` notification is received from Core. The first time the `hmiLevel` is `FULL` attempt to subscribe to system capabilty updates. - * - * @param notification The `OnHMIStatus` notification received from Core - */ -- (void)sdl_hmiStatusNotification:(SDLRPCNotificationNotification *)notification { - SDLOnHMIStatus *hmiStatus = (SDLOnHMIStatus *)notification.notification; - - if (hmiStatus.windowID != nil && hmiStatus.windowID.integerValue != SDLPredefinedWindowsDefaultWindow) { - return; - } - - if (self.isFirstHMILevelFull || ![hmiStatus.hmiLevel isEqualToEnum:SDLHMILevelFull]) { - return; - } - - self.isFirstHMILevelFull = YES; - [self sdl_subscribeToSystemCapabilityUpdates]; -} - #pragma mark - Window And Display Capabilities - (nullable SDLWindowCapability *)windowCapabilityWithWindowID:(NSUInteger)windowID { @@ -387,6 +273,36 @@ - (void)sdl_updateDeprecatedDisplayCapabilities { #pragma mark - System Capability Updates +- (BOOL)isCapabilitySupported:(SDLSystemCapabilityType)type { + if ([self sdl_cachedCapabilityForType:type] != nil) { + return YES; + } else if (self.hmiCapabilities != nil) { + SDLHMICapabilities *hmiCapabilities = self.hmiCapabilities; + } + + return NO; +} + +- (nullable SDLSystemCapability *)sdl_cachedCapabilityForType:(SDLSystemCapabilityType)type { + if ([type isEqualToEnum:SDLSystemCapabilityTypePhoneCall] && self.phoneCapability != nil) { + return [[SDLSystemCapability alloc] initWithPhoneCapability:self.phoneCapability]; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeNavigation] && self.navigationCapability != nil) { + return [[SDLSystemCapability alloc] initWithNavigationCapability:self.navigationCapability]; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeAppServices] && self.appServicesCapabilities != nil) { + return [[SDLSystemCapability alloc] initWithAppServicesCapabilities:self.appServicesCapabilities]; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeDisplays] && self.displays != nil) { + return [[SDLSystemCapability alloc] initWithDisplayCapabilities:self.displays]; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeSeatLocation] && self.seatLocationCapability != nil) { + return [[SDLSystemCapability alloc] initWithSeatLocationCapability:self.seatLocationCapability]; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeRemoteControl] && self.remoteControlCapability != nil) { + return [[SDLSystemCapability alloc] initWithRemoteControlCapability:self.remoteControlCapability]; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeVideoStreaming] && self.videoStreamingCapability != nil) { + return [[SDLSystemCapability alloc] initWithVideoStreamingCapability:self.videoStreamingCapability]; + } else { + return nil; + } +} + - (void)updateCapabilityType:(SDLSystemCapabilityType)type completionHandler:(SDLUpdateCapabilityHandler)handler { if (self.supportsSubscriptions) { // Just return the cached data because we get `onSystemCapability` callbacks @@ -629,6 +545,120 @@ - (void)sdl_callObserversForCapabilityUpdate:(SDLSystemCapability *)capability h handler(nil, self); } +#pragma mark - Notifications + +/** + * Registers for notifications and responses from Core + */ +- (void)sdl_registerForNotifications { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_registerResponse:) name:SDLDidReceiveRegisterAppInterfaceResponse object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_displayLayoutResponse:) name:SDLDidReceiveSetDisplayLayoutResponse object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_systemCapabilityUpdatedNotification:) name:SDLDidReceiveSystemCapabilityUpdatedNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_systemCapabilityResponseNotification:) name:SDLDidReceiveGetSystemCapabilitiesResponse object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_hmiStatusNotification:) name:SDLDidChangeHMIStatusNotification object:nil]; +} + +/** + * Called when a `RegisterAppInterfaceResponse` response is received from Core. The head unit capabilities are saved. + * + * @param notification The `RegisterAppInterfaceResponse` response received from Core + */ +- (void)sdl_registerResponse:(SDLRPCResponseNotification *)notification { + SDLRegisterAppInterfaceResponse *response = (SDLRegisterAppInterfaceResponse *)notification.response; + if (!response.success.boolValue) { return; } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" + self.displayCapabilities = response.displayCapabilities; + self.softButtonCapabilities = response.softButtonCapabilities; + self.buttonCapabilities = response.buttonCapabilities; + self.presetBankCapabilities = response.presetBankCapabilities; +#pragma clang diagnostic pop + + self.hmiCapabilities = response.hmiCapabilities; + self.hmiZoneCapabilities = response.hmiZoneCapabilities; + self.speechCapabilities = response.speechCapabilities; + self.prerecordedSpeechCapabilities = response.prerecordedSpeech; + self.vrCapability = (response.vrCapabilities.count > 0 && [response.vrCapabilities.firstObject isEqualToEnum:SDLVRCapabilitiesText]) ? YES : NO; + self.audioPassThruCapabilities = response.audioPassThruCapabilities; + self.pcmStreamCapability = response.pcmStreamCapabilities; + + self.shouldConvertDeprecatedDisplayCapabilities = YES; + self.displays = [self sdl_createDisplayCapabilityListFromRegisterResponse:response]; + + // call the observers in case the new display capability list is created from deprecated types + SDLSystemCapability *systemCapability = [[SDLSystemCapability alloc] initWithDisplayCapabilities:self.displays]; + [self sdl_callObserversForCapabilityUpdate:systemCapability handler:nil]; +} + +/** + * Called when a `SetDisplayLayoutResponse` response is received from Core. If the template was set successfully, the the new capabilities for the template are saved. + * + * @param notification The `SetDisplayLayoutResponse` response received from Core + */ +- (void)sdl_displayLayoutResponse:(SDLRPCResponseNotification *)notification { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" + SDLSetDisplayLayoutResponse *response = (SDLSetDisplayLayoutResponse *)notification.response; +#pragma clang diagnostic pop + if (!response.success.boolValue) { return; } + + // If we've received a display capability update then we should not convert our deprecated display capabilities and we should just return + if (!self.shouldConvertDeprecatedDisplayCapabilities) { return; } + + self.displayCapabilities = response.displayCapabilities; + self.buttonCapabilities = response.buttonCapabilities; + self.softButtonCapabilities = response.softButtonCapabilities; + self.presetBankCapabilities = response.presetBankCapabilities; + + self.displays = [self sdl_createDisplayCapabilityListFromSetDisplayLayoutResponse:response]; + + // Call the observers in case the new display capability list is created from deprecated types + SDLSystemCapability *systemCapability = [[SDLSystemCapability alloc] initWithDisplayCapabilities:self.displays]; + [self sdl_callObserversForCapabilityUpdate:systemCapability handler:nil]; +} + + +/** + * Called when an `OnSystemCapabilityUpdated` notification is received from Core. The updated system capabilty is saved. + * + * @param notification The `OnSystemCapabilityUpdated` notification received from Core + */ +- (void)sdl_systemCapabilityUpdatedNotification:(SDLRPCNotificationNotification *)notification { + SDLOnSystemCapabilityUpdated *systemCapabilityUpdatedNotification = (SDLOnSystemCapabilityUpdated *)notification.notification; + [self sdl_saveSystemCapability:systemCapabilityUpdatedNotification.systemCapability completionHandler:nil]; +} + +/** + Called with a `GetSystemCapabilityResponse` notification is received from core. The updated system capability is saved. + + @param notification The `GetSystemCapabilityResponse` notification received from Core + */ +- (void)sdl_systemCapabilityResponseNotification:(SDLRPCResponseNotification *)notification { + SDLGetSystemCapabilityResponse *systemCapabilityResponse = (SDLGetSystemCapabilityResponse *)notification.response; + [self sdl_saveSystemCapability:systemCapabilityResponse.systemCapability completionHandler:nil]; +} + +/** + * Called when an `OnHMIStatus` notification is received from Core. The first time the `hmiLevel` is `FULL` attempt to subscribe to system capabilty updates. + * + * @param notification The `OnHMIStatus` notification received from Core + */ +- (void)sdl_hmiStatusNotification:(SDLRPCNotificationNotification *)notification { + SDLOnHMIStatus *hmiStatus = (SDLOnHMIStatus *)notification.notification; + + if (hmiStatus.windowID != nil && hmiStatus.windowID.integerValue != SDLPredefinedWindowsDefaultWindow) { + return; + } + + if (self.isFirstHMILevelFull || ![hmiStatus.hmiLevel isEqualToEnum:SDLHMILevelFull]) { + return; + } + + self.isFirstHMILevelFull = YES; + [self sdl_subscribeToSystemCapabilityUpdates]; +} + @end NS_ASSUME_NONNULL_END From 320c9412c41dd4ebca774c0b3071a88671b32b93 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 30 Jan 2020 13:11:01 -0500 Subject: [PATCH 02/39] Implement isCapabilitySupported --- SmartDeviceLink/SDLSystemCapabilityManager.m | 30 +++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 3f772c1b0..8b7838a10 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -19,6 +19,7 @@ #import "SDLGetSystemCapability.h" #import "SDLGetSystemCapabilityResponse.h" #import "SDLGlobals.h" +#import "SDLHMICapabilities.h" #import "SDLLogMacros.h" #import "SDLNavigationCapability.h" #import "SDLNotificationConstants.h" @@ -277,9 +278,36 @@ - (BOOL)isCapabilitySupported:(SDLSystemCapabilityType)type { if ([self sdl_cachedCapabilityForType:type] != nil) { return YES; } else if (self.hmiCapabilities != nil) { - SDLHMICapabilities *hmiCapabilities = self.hmiCapabilities; + if ([type isEqualToEnum:SDLSystemCapabilityTypePhoneCall]) { + return self.hmiCapabilities.phoneCall.boolValue; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeNavigation]) { + return self.hmiCapabilities.navigation.boolValue; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { + return self.hmiCapabilities.displays.boolValue; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeRemoteControl]) { + return self.hmiCapabilities.remoteControl.boolValue; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeSeatLocation]) { + return self.hmiCapabilities.seatLocation.boolValue; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeAppServices]) { + //This is a corner case that the param was not available in 5.1.0, but the app services feature was available. We have to say it's available because we don't know. + if ([[SDLGlobals sharedGlobals].rpcVersion isEqualToVersion:[SDLVersion versionWithString:@"5.1.0"]]) { + return YES; + } + + return self.hmiCapabilities.appServices.boolValue; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeVideoStreaming]) { + if ([[SDLGlobals sharedGlobals].rpcVersion isGreaterThanOrEqualToVersion:[SDLVersion versionWithString:@"3.0.0"]] && [[SDLGlobals sharedGlobals].rpcVersion isLessThanOrEqualToVersion:[SDLVersion versionWithString:@"4.4.0"]]) { + // This was before the system capability feature was added so check if graphics are supported instead using the deprecated display capabilities + return self.displayCapabilities.graphicSupported; + } + + return self.hmiCapabilities.videoStreaming.boolValue; + } else { + return NO; + } } + return NO; } From e19f5e002d7132b56fd10cf013ad93b9aaa99c34 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 31 Jan 2020 11:32:59 -0500 Subject: [PATCH 03/39] Continued work on subscriptions * Working on making them return with data if subscriptions are not supported as well --- SmartDeviceLink/SDLSystemCapabilityManager.h | 41 ++++-- SmartDeviceLink/SDLSystemCapabilityManager.m | 119 ++++++++++-------- SmartDeviceLink/SDLSystemCapabilityObserver.h | 9 ++ SmartDeviceLink/SDLSystemCapabilityObserver.m | 10 ++ 4 files changed, 115 insertions(+), 64 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.h b/SmartDeviceLink/SDLSystemCapabilityManager.h index 8d76e8551..418c856ee 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.h +++ b/SmartDeviceLink/SDLSystemCapabilityManager.h @@ -54,9 +54,10 @@ typedef void (^SDLCapabilityUpdateHandler)(SDLSystemCapability *capability); An observer block for whenever a subscription or value is retrieved. @param capability The capability that was updated. + @param subscribed Whether or not the capability was subscribed or if the capability will only be pulled once. @param error An error that occurred. */ -typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability *capability, NSError *error); +typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nullable capability, BOOL subscribed, NSError * _Nullable error); /** A manager that handles updating and subscribing to SDL capabilities. @@ -252,35 +253,49 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability *capabil * @param type The type of capability to retrieve * @param handler The handler to be called when the retrieval is complete */ -- (void)updateCapabilityType:(SDLSystemCapabilityType)type completionHandler:(SDLUpdateCapabilityHandler)handler; +- (void)updateCapabilityType:(SDLSystemCapabilityType)type completionHandler:(SDLUpdateCapabilityHandler)handler __deprecated_msg("use a subscribe method instead, which will return a single new value if subscriptions are not available"); + /// Returns whether or not the capability type is supported on the system. You can use this to check if subscribing to the capability will work. /// @param type The SystemCapabilityType that will be checked. /// @return Whether or not `type` is supported by the connected head unit. - (BOOL)isCapabilitySupported:(SDLSystemCapabilityType)type; -/** - Subscribe to a particular capability type using a block callback +/// Subscribe to a particular capability type using a block callback. - @param type The type of capability to subscribe to - @param block The block to be called when the capability is updated - @return An object that can be used to unsubscribe the block using unsubscribeFromCapabilityType:withObserver: by passing it in the observer callback, or nil if subscriptions aren't available on this head unit - */ -- (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withBlock:(SDLCapabilityUpdateHandler)block; +/// This method will be called immediately with the current value and called every time the value is updated on RPC v5.1.0+ systems (`supportsSubscriptions == YES`). If this method is called on a sub-v5.1.0 system (`supportsSubscriptions == NO`), the method will return `NO` and the selector will never be called, unless the `type` is `DISPLAYS` which is supported on every version. + +/// @param type The type of capability to subscribe to +/// @param block The block to be called when the capability is updated +/// @return An object that can be used to unsubscribe the block using unsubscribeFromCapabilityType:withObserver: by passing it in the observer callback, or nil if subscriptions aren't available on this head unit +- (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withBlock:(SDLCapabilityUpdateHandler)block __deprecated_msg("use subscribeToCapabilityType:withUpdateBlock: instead"); + +/// Subscribe to a particular capability type using a block callback. + +/// This method will be called immediately with the current value and called every time the value is updated on RPC v5.1.0+ systems (`supportsSubscriptions == YES`). If this method is called on a sub-v5.1.0 system (`supportsSubscriptions == NO`), the method will return `NO` and the selector will never be called, unless the `type` is `DISPLAYS` which is supported on every version. + +/// @param type The type of capability to subscribe to +/// @param block The block to be called when the capability is updated with an error if one occurs +/// @return An object that can be used to unsubscribe the block using unsubscribeFromCapabilityType:withObserver: by passing it in the observer callback, or nil if subscriptions aren't available on this head unit +- (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withUpdateBlock:(SDLCapabilityUpdateWithErrorHandler)block; /** - * Subscribe to a particular capability type with a selector callback. The selector supports the following parameters: + * Subscribe to a particular capability type with a selector callback. + * + * The selector supports the following parameters: * * 1. No parameters e.g. `- (void)phoneCapabilityUpdated;` + * * 2. One `SDLSystemCapability *` parameter, e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability` - * 3. Two parameters, one `SDLSystemCapability *` parameter, and one `NSError *` parameter, e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability error:(NSError *)error` * - * This method will be called immediately with the current value and called every time the value is updated on RPC v5.1.0+ systems (`supportsSubscriptions == YES`). If this method is called on a sub-v5.1.0 system (`supportsSubscriptions == NO`), the method will return `NO` and the selector will never be called. + * 3. Two parameters, one `SDLSystemCapability *` parameter, and one `BOOL` parameter, e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability error:(NSError *)error` + * + * This method will be called immediately with the current value and called every time the value is updated on RPC v5.1.0+ systems (`supportsSubscriptions == YES`). If this method is called on a sub-v5.1.0 system (`supportsSubscriptions == NO`), the method will return `NO` and the selector will never be called, unless the `type` is `DISPLAYS` which is supported on every version. * * @param type The type of the system capability to subscribe to * @param observer The object that will have `selector` called whenever the capability is updated * @param selector The selector on `observer` that will be called whenever the capability is updated - * @return Whether or not the subscription succeeded. `NO` if the connected system doesn't support capability subscriptions, or if the `selector` doesn't support the correct parameters (see above) + * @return Whether or not the subscription succeeded. `NO` if the connected system doesn't support capability subscriptions, or if the `selector` doesn't support the correct parameters (see above). */ - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id)observer selector:(SEL)selector; diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 8b7838a10..707a1ac52 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -70,6 +70,7 @@ @interface SDLSystemCapabilityManager () @property (assign, nonatomic, readwrite) BOOL supportsSubscriptions; @property (strong, nonatomic) NSMutableDictionary *> *capabilityObservers; +@property (strong, nonatomic) NSMutableDictionary *> *subscriptionStatus; @property (nullable, strong, nonatomic) SDLSystemCapability *lastReceivedCapability; @@ -95,22 +96,14 @@ - (instancetype)initWithConnectionManager:(id)manager _appServicesCapabilitiesDictionary = [NSMutableDictionary dictionary]; _capabilityObservers = [NSMutableDictionary dictionary]; - for (SDLSystemCapabilityType capabilityType in [self.class sdl_systemCapabilityTypes]) { - _capabilityObservers[capabilityType] = [NSMutableArray array]; - } + _subscriptionStatus = [NSMutableDictionary dictionary]; [self sdl_registerForNotifications]; return self; } -- (void)start { - SDLVersion *onSystemCapabilityNotificationRPCVersion = [SDLVersion versionWithString:@"5.1.0"]; - SDLVersion *headUnitRPCVersion = SDLGlobals.sharedGlobals.rpcVersion; - if ([headUnitRPCVersion isGreaterThanOrEqualToVersion:onSystemCapabilityNotificationRPCVersion]) { - _supportsSubscriptions = YES; - } -} +- (void)start { } /** * Resets the capabilities when a transport session is closed. @@ -137,9 +130,8 @@ - (void)stop { _appServicesCapabilitiesDictionary = [NSMutableDictionary dictionary]; _supportsSubscriptions = NO; - for (SDLSystemCapabilityType capabilityType in [self.class sdl_systemCapabilityTypes]) { - _capabilityObservers[capabilityType] = [NSMutableArray array]; - } + [_capabilityObservers removeAllObjects]; + [_subscriptionStatus removeAllObjects]; _isFirstHMILevelFull = NO; _shouldConvertDeprecatedDisplayCapabilities = YES; @@ -147,6 +139,10 @@ - (void)stop { #pragma mark - Getters +- (BOOL)supportsSubscriptions { + return [[SDLGlobals sharedGlobals].rpcVersion isGreaterThanOrEqualToVersion:[SDLVersion versionWithString:@"5.1.0"]]; +} + - (nullable SDLAppServicesCapabilities *)appServicesCapabilities { if (self.appServicesCapabilitiesDictionary.count == 0) { return nil; } @@ -337,52 +333,41 @@ - (void)updateCapabilityType:(SDLSystemCapabilityType)type completionHandler:(SD handler(nil, self); } else { // Go and get the actual data - SDLGetSystemCapability *getSystemCapability = [[SDLGetSystemCapability alloc] initWithType:type]; - [self sdl_sendGetSystemCapability:getSystemCapability completionHandler:handler]; + __weak typeof(self) weakself = self; + [self sdl_sendGetSystemCapabilityWithType:type subscribe:nil completionHandler:^(SDLSystemCapability * _Nonnull capability, BOOL subscribed, NSError * _Nonnull error) { + handler(error, weakself); + }]; } } -/** - * A list of all possible system capability types. - * - * @return An array of all possible system capability types - */ -+ (NSArray *)sdl_systemCapabilityTypes { - return @[SDLSystemCapabilityTypeAppServices, SDLSystemCapabilityTypeNavigation, SDLSystemCapabilityTypePhoneCall, SDLSystemCapabilityTypeVideoStreaming, SDLSystemCapabilityTypeRemoteControl, SDLSystemCapabilityTypeDisplays, SDLSystemCapabilityTypeSeatLocation]; -} - # pragma mark Subscribing -/** - * Sends a subscribe request for all possible system capabilites. If connecting to Core versions 4.5+, the requested capability will be returned in the response. If connecting to Core versions 5.1+, the manager will received `OnSystemCapabilityUpdated` notifications when the capability updates if the subscription was successful. - */ -- (void)sdl_subscribeToSystemCapabilityUpdates { - for (SDLSystemCapabilityType type in [self.class sdl_systemCapabilityTypes]) { - SDLGetSystemCapability *getSystemCapability = [[SDLGetSystemCapability alloc] initWithType:type]; - if (self.supportsSubscriptions) { - getSystemCapability.subscribe = @YES; - } - - [self sdl_sendGetSystemCapability:getSystemCapability completionHandler:nil]; - } -} - -/** - * Sends a `GetSystemCapability` to Core and handles the response by saving the returned data and notifying the subscriber. - * - * @param getSystemCapability The `GetSystemCapability` request to send - */ -- (void)sdl_sendGetSystemCapability:(SDLGetSystemCapability *)getSystemCapability completionHandler:(nullable SDLUpdateCapabilityHandler)handler { +/// Sends a GetSystemCapability and sends back the response +/// @param type The type to get +/// @param subscribe Whether to change the subscription status +/// @param handler The handler to be returned +- (void)sdl_sendGetSystemCapabilityWithType:(SDLSystemCapabilityType)type subscribe:(nullable NSNumber *)subscribe completionHandler:(nullable SDLCapabilityUpdateWithErrorHandler)handler { __weak typeof(self) weakSelf = self; + SDLGetSystemCapability *getSystemCapability = [[SDLGetSystemCapability alloc] initWithType:type]; + getSystemCapability.subscribe = subscribe; + [self.connectionManager sendConnectionRequest:getSystemCapability withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { + if (![response isKindOfClass:[SDLGetSystemCapabilityResponse class]]) { + if (handler == nil) { return; } + handler(nil, NO, error); + return; + } + if (error != nil) { // An error is returned if the request was unsuccessful or if a Generic Response was returned if (handler == nil) { return; } - handler(error, weakSelf); + handler(nil, NO, error); return; } SDLGetSystemCapabilityResponse *getSystemCapabilityResponse = (SDLGetSystemCapabilityResponse *)response; + + // TODO: Keep track of subscriptions [weakSelf sdl_saveSystemCapability:getSystemCapabilityResponse.systemCapability completionHandler:handler]; }]; } @@ -396,7 +381,7 @@ - (void)sdl_sendGetSystemCapability:(SDLGetSystemCapability *)getSystemCapabilit @param handler The handler to be called when the save completes @return Whether or not the save occurred. This can be `NO` if the new system capability is equivalent to the old capability. */ -- (BOOL)sdl_saveSystemCapability:(SDLSystemCapability *)systemCapability completionHandler:(nullable SDLUpdateCapabilityHandler)handler { +- (BOOL)sdl_saveSystemCapability:(SDLSystemCapability *)systemCapability completionHandler:(nullable SDLCapabilityUpdateWithErrorHandler)handler { if ([self.lastReceivedCapability isEqual:systemCapability]) { [self sdl_callObserversForCapabilityUpdate:systemCapability handler:handler]; return NO; @@ -516,19 +501,50 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n if (!self.supportsSubscriptions && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { return nil; } SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:[[NSObject alloc] init] block:block]; + + if (self.capabilityObservers[type] == nil) { + self.capabilityObservers[type] = [NSMutableArray array]; + + // TODO: Subscribe + } [self.capabilityObservers[type] addObject:observerObject]; return observerObject.observer; } -- (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id)observer selector:(SEL)selector { +- (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withUpdateBlock:(SDLCapabilityUpdateWithErrorHandler)block { // DISPLAYS always works due to old-style SetDisplayLayoutRepsonse updates, but otherwise, subscriptions won't work - if (!self.supportsSubscriptions && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { return NO; } + if (!self.supportsSubscriptions && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { return nil; } + + SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:[[NSObject alloc] init] updateHandler:block]; + + if (self.capabilityObservers[type] == nil) { + self.capabilityObservers[type] = [NSMutableArray array]; + + // TODO: Subscribe + } + [self.capabilityObservers[type] addObject:observerObject]; + + return observerObject.observer; +} +- (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id)observer selector:(SEL)selector { NSUInteger numberOfParametersInSelector = [NSStringFromSelector(selector) componentsSeparatedByString:@":"].count - 1; - if (numberOfParametersInSelector > 1) { return NO; } + if (numberOfParametersInSelector > 2) { return NO; } + + // TODO: If it doesn't support subscriptions, get the data only once, then return NO + // DISPLAYS always works due to old-style SetDisplayLayoutResponse updates, but otherwise, subscriptions won't work + if (!self.supportsSubscriptions && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { return NO; } + + // TODO: If the first subscription to the type, subscribe to it in `GetSystemCapability` SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:observer selector:selector]; + + if (self.capabilityObservers[type] == nil) { + self.capabilityObservers[type] = [NSMutableArray array]; + + // TODO: Subscribe + } [self.capabilityObservers[type] addObject:observerObject]; return YES; @@ -538,6 +554,7 @@ - (void)unsubscribeFromCapabilityType:(SDLSystemCapabilityType)type withObserver for (SDLSystemCapabilityObserver *capabilityObserver in self.capabilityObservers[type]) { if ([observer isEqual:capabilityObserver.observer]) { [self.capabilityObservers[type] removeObject:capabilityObserver]; + // TODO: If this was the last observer, unsubscribe from the HU break; } } @@ -546,7 +563,7 @@ - (void)unsubscribeFromCapabilityType:(SDLSystemCapabilityType)type withObserver /// 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 -- (void)sdl_callObserversForCapabilityUpdate:(SDLSystemCapability *)capability handler:(nullable SDLUpdateCapabilityHandler)handler { +- (void)sdl_callObserversForCapabilityUpdate:(SDLSystemCapability *)capability handler:(nullable SDLCapabilityUpdateWithErrorHandler)handler { for (SDLSystemCapabilityObserver *observer in self.capabilityObservers[capability.systemCapabilityType]) { if (observer.block != nil) { observer.block(capability); @@ -570,7 +587,7 @@ - (void)sdl_callObserversForCapabilityUpdate:(SDLSystemCapability *)capability h } if (handler == nil) { return; } - handler(nil, self); + handler(capability, NO, nil); // TODO: Subscribed or not needs to say YES if we're subscribed } #pragma mark - Notifications diff --git a/SmartDeviceLink/SDLSystemCapabilityObserver.h b/SmartDeviceLink/SDLSystemCapabilityObserver.h index f5450d60e..37819c6dc 100644 --- a/SmartDeviceLink/SDLSystemCapabilityObserver.h +++ b/SmartDeviceLink/SDLSystemCapabilityObserver.h @@ -14,6 +14,8 @@ NS_ASSUME_NONNULL_BEGIN typedef void (^SDLCapabilityUpdateHandler)(SDLSystemCapability *capability); +typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability *capability, NSError *error); + /** An observer object for SDLSystemCapabilityManager */ @@ -52,6 +54,13 @@ typedef void (^SDLCapabilityUpdateHandler)(SDLSystemCapability *capability); */ - (instancetype)initWithObserver:(id)observer block:(SDLCapabilityUpdateHandler)block; +/// Create an observer using an object and a callback block + +/// @param observer The object that can be used to unsubscribe the block +/// @param block The block that will be called when the subscription triggers +/// @return The observer +- (instancetype)initWithObserver:(id)observer updateHandler:(SDLCapabilityUpdateWithErrorHandler)block; + @end NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLSystemCapabilityObserver.m b/SmartDeviceLink/SDLSystemCapabilityObserver.m index 7ccbd38af..2b782540a 100644 --- a/SmartDeviceLink/SDLSystemCapabilityObserver.m +++ b/SmartDeviceLink/SDLSystemCapabilityObserver.m @@ -32,6 +32,16 @@ - (instancetype)initWithObserver:(id)observer block:(SDLCapabilityUpda return self; } +- (instancetype)initWithObserver:(id)observer block:(SDLCapabilityUpdateWithErrorHandler)block { + self = [super init]; + if (!self) { return nil; } + + _observer = observer; + _block = block; + + return self; +} + - (NSString *)description { if (self.selector) { return [NSString stringWithFormat:@"Observer: %@[%@] - %@", [_observer class], _observer, NSStringFromSelector(_selector)]; From 95df999a719b126ceb664d0f0f404a48ab8cdc01 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 31 Jan 2020 15:07:16 -0500 Subject: [PATCH 04/39] Rejiggering to return errors --- SmartDeviceLink/SDLSystemCapabilityManager.h | 4 +- SmartDeviceLink/SDLSystemCapabilityManager.m | 98 ++++++++----------- SmartDeviceLink/SDLSystemCapabilityObserver.h | 9 +- SmartDeviceLink/SDLSystemCapabilityObserver.m | 4 +- 4 files changed, 54 insertions(+), 61 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.h b/SmartDeviceLink/SDLSystemCapabilityManager.h index 418c856ee..bfd96a0bd 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.h +++ b/SmartDeviceLink/SDLSystemCapabilityManager.h @@ -288,7 +288,9 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla * * 2. One `SDLSystemCapability *` parameter, e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability` * - * 3. Two parameters, one `SDLSystemCapability *` parameter, and one `BOOL` parameter, e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability error:(NSError *)error` + * 3. Two parameters, one `SDLSystemCapability *` parameter, and one `NSError` parameter, e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability error:(NSError *)error` + * + * 4. Three parameters, one `SDLSystemCapability *` parameter, one `NSError` parameter, and one `BOOL` parameter e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability error:(NSError *)error subscribed:(BOOL)subscribed` * * This method will be called immediately with the current value and called every time the value is updated on RPC v5.1.0+ systems (`supportsSubscriptions == YES`). If this method is called on a sub-v5.1.0 system (`supportsSubscriptions == NO`), the method will return `NO` and the selector will never be called, unless the `type` is `DISPLAYS` which is supported on every version. * diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 707a1ac52..5cedbdefe 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -74,8 +74,6 @@ @interface SDLSystemCapabilityManager () @property (nullable, strong, nonatomic) SDLSystemCapability *lastReceivedCapability; -@property (assign, nonatomic) BOOL isFirstHMILevelFull; - @property (assign, nonatomic) BOOL shouldConvertDeprecatedDisplayCapabilities; @end @@ -91,7 +89,6 @@ - (instancetype)initWithConnectionManager:(id)manager } _connectionManager = manager; - _isFirstHMILevelFull = NO; _shouldConvertDeprecatedDisplayCapabilities = YES; _appServicesCapabilitiesDictionary = [NSMutableDictionary dictionary]; @@ -133,7 +130,6 @@ - (void)stop { [_capabilityObservers removeAllObjects]; [_subscriptionStatus removeAllObjects]; - _isFirstHMILevelFull = NO; _shouldConvertDeprecatedDisplayCapabilities = YES; } @@ -368,7 +364,7 @@ - (void)sdl_sendGetSystemCapabilityWithType:(SDLSystemCapabilityType)type subscr SDLGetSystemCapabilityResponse *getSystemCapabilityResponse = (SDLGetSystemCapabilityResponse *)response; // TODO: Keep track of subscriptions - [weakSelf sdl_saveSystemCapability:getSystemCapabilityResponse.systemCapability completionHandler:handler]; + [weakSelf sdl_saveSystemCapability:getSystemCapabilityResponse.systemCapability error:error completionHandler:handler]; }]; } @@ -381,9 +377,9 @@ - (void)sdl_sendGetSystemCapabilityWithType:(SDLSystemCapabilityType)type subscr @param handler The handler to be called when the save completes @return Whether or not the save occurred. This can be `NO` if the new system capability is equivalent to the old capability. */ -- (BOOL)sdl_saveSystemCapability:(SDLSystemCapability *)systemCapability completionHandler:(nullable SDLCapabilityUpdateWithErrorHandler)handler { +- (BOOL)sdl_saveSystemCapability:(nullable SDLSystemCapability *)systemCapability error:(nullable NSError *)error completionHandler:(nullable SDLCapabilityUpdateWithErrorHandler)handler { if ([self.lastReceivedCapability isEqual:systemCapability]) { - [self sdl_callObserversForCapabilityUpdate:systemCapability handler:handler]; + [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:error handler:handler]; return NO; } self.lastReceivedCapability = systemCapability; @@ -392,31 +388,31 @@ - (BOOL)sdl_saveSystemCapability:(SDLSystemCapability *)systemCapability complet if ([systemCapabilityType isEqualToEnum:SDLSystemCapabilityTypePhoneCall]) { if ([self.phoneCapability isEqual:systemCapability.phoneCapability]) { - [self sdl_callObserversForCapabilityUpdate:systemCapability handler:handler]; + [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:error handler:handler]; return NO; } self.phoneCapability = systemCapability.phoneCapability; } else if ([systemCapabilityType isEqualToEnum:SDLSystemCapabilityTypeNavigation]) { if ([self.navigationCapability isEqual:systemCapability.navigationCapability]) { - [self sdl_callObserversForCapabilityUpdate:systemCapability handler:handler]; + [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:error handler:handler]; return NO; } self.navigationCapability = systemCapability.navigationCapability; } else if ([systemCapabilityType isEqualToEnum:SDLSystemCapabilityTypeRemoteControl]) { if ([self.remoteControlCapability isEqual:systemCapability.remoteControlCapability]) { - [self sdl_callObserversForCapabilityUpdate:systemCapability handler:handler]; + [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:error handler:handler]; return NO; } self.remoteControlCapability = systemCapability.remoteControlCapability; } else if ([systemCapabilityType isEqualToEnum:SDLSystemCapabilityTypeSeatLocation]) { if ([self.seatLocationCapability isEqual:systemCapability.seatLocationCapability]) { - [self sdl_callObserversForCapabilityUpdate:systemCapability handler:handler]; + [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:error handler:handler]; return NO; } self.seatLocationCapability = systemCapability.seatLocationCapability; } else if ([systemCapabilityType isEqualToEnum:SDLSystemCapabilityTypeVideoStreaming]) { if ([self.videoStreamingCapability isEqual:systemCapability.videoStreamingCapability]) { - [self sdl_callObserversForCapabilityUpdate:systemCapability handler:handler]; + [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:error handler:handler]; return NO; } self.videoStreamingCapability = systemCapability.videoStreamingCapability; @@ -433,7 +429,7 @@ - (BOOL)sdl_saveSystemCapability:(SDLSystemCapability *)systemCapability complet SDLLogD(@"Updated system capability manager with new data: %@", systemCapability); - [self sdl_callObserversForCapabilityUpdate:systemCapability handler:handler]; + [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:error handler:handler]; return YES; } @@ -563,26 +559,40 @@ - (void)unsubscribeFromCapabilityType:(SDLSystemCapabilityType)type withObserver /// 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 -- (void)sdl_callObserversForCapabilityUpdate:(SDLSystemCapability *)capability handler:(nullable SDLCapabilityUpdateWithErrorHandler)handler { - for (SDLSystemCapabilityObserver *observer in self.capabilityObservers[capability.systemCapabilityType]) { +- (void)sdl_callObserversForType:(SDLSystemCapabilityType)type update:(nullable SDLSystemCapability *)capability error:(nullable NSError *)error handler:(nullable SDLCapabilityUpdateWithErrorHandler)handler { + for (SDLSystemCapabilityObserver *observer in self.capabilityObservers[type]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" if (observer.block != nil) { observer.block(capability); +#pragma clang diagnostic pop + } else if (observer.updateBlock != nil) { + observer.updateBlock(capability, NO, error); // TODO: Subscribed based on whether we are subscribed } else { + if (![observer respondsToSelector:observer.selector]) { + @throw [NSException sdl_invalidSelectorExceptionWithSelector:observer.selector]; + } + + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[(NSObject *)observer.observer methodSignatureForSelector:observer.selector]]; + [invocation setSelector:observer.selector]; + [invocation setTarget:observer.observer]; + NSUInteger numberOfParametersInSelector = [NSStringFromSelector(observer.selector) componentsSeparatedByString:@":"].count - 1; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" - if (numberOfParametersInSelector == 0) { - if ([observer.observer respondsToSelector:observer.selector]) { - [observer.observer performSelector:observer.selector]; - } - } else if (numberOfParametersInSelector == 1) { - if ([observer.observer respondsToSelector:observer.selector]) { - [observer.observer performSelector:observer.selector withObject:capability]; - } - } else { + if (numberOfParametersInSelector >= 1) { + [invocation setArgument:&capability atIndex:2]; + } + if (numberOfParametersInSelector >= 2) { + [invocation setArgument:&error atIndex:3]; + } + if (numberOfParametersInSelector >= 3) { + BOOL argVal = NO; // TODO: Send actual subscription status + [invocation setArgument:&argVal atIndex:4]; + } + if (numberOfParametersInSelector >= 4) { @throw [NSException sdl_invalidSelectorExceptionWithSelector:observer.selector]; } -#pragma clang diagnostic pop + + [invocation invoke]; } } @@ -592,9 +602,7 @@ - (void)sdl_callObserversForCapabilityUpdate:(SDLSystemCapability *)capability h #pragma mark - Notifications -/** - * Registers for notifications and responses from Core - */ +/// Registers for notifications and responses from Core - (void)sdl_registerForNotifications { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_registerResponse:) name:SDLDidReceiveRegisterAppInterfaceResponse object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_displayLayoutResponse:) name:SDLDidReceiveSetDisplayLayoutResponse object:nil]; @@ -631,9 +639,9 @@ - (void)sdl_registerResponse:(SDLRPCResponseNotification *)notification { self.shouldConvertDeprecatedDisplayCapabilities = YES; self.displays = [self sdl_createDisplayCapabilityListFromRegisterResponse:response]; - // call the observers in case the new display capability list is created from deprecated types + // Call the observers in case the new display capability list is created from deprecated types SDLSystemCapability *systemCapability = [[SDLSystemCapability alloc] initWithDisplayCapabilities:self.displays]; - [self sdl_callObserversForCapabilityUpdate:systemCapability handler:nil]; + [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:nil handler:nil]; } /** @@ -660,7 +668,7 @@ - (void)sdl_displayLayoutResponse:(SDLRPCResponseNotification *)notification { // Call the observers in case the new display capability list is created from deprecated types SDLSystemCapability *systemCapability = [[SDLSystemCapability alloc] initWithDisplayCapabilities:self.displays]; - [self sdl_callObserversForCapabilityUpdate:systemCapability handler:nil]; + [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:nil handler:nil]; } @@ -671,7 +679,7 @@ - (void)sdl_displayLayoutResponse:(SDLRPCResponseNotification *)notification { */ - (void)sdl_systemCapabilityUpdatedNotification:(SDLRPCNotificationNotification *)notification { SDLOnSystemCapabilityUpdated *systemCapabilityUpdatedNotification = (SDLOnSystemCapabilityUpdated *)notification.notification; - [self sdl_saveSystemCapability:systemCapabilityUpdatedNotification.systemCapability completionHandler:nil]; + [self sdl_saveSystemCapability:systemCapabilityUpdatedNotification.systemCapability error:nil completionHandler:nil]; } /** @@ -681,27 +689,7 @@ - (void)sdl_systemCapabilityUpdatedNotification:(SDLRPCNotificationNotification */ - (void)sdl_systemCapabilityResponseNotification:(SDLRPCResponseNotification *)notification { SDLGetSystemCapabilityResponse *systemCapabilityResponse = (SDLGetSystemCapabilityResponse *)notification.response; - [self sdl_saveSystemCapability:systemCapabilityResponse.systemCapability completionHandler:nil]; -} - -/** - * Called when an `OnHMIStatus` notification is received from Core. The first time the `hmiLevel` is `FULL` attempt to subscribe to system capabilty updates. - * - * @param notification The `OnHMIStatus` notification received from Core - */ -- (void)sdl_hmiStatusNotification:(SDLRPCNotificationNotification *)notification { - SDLOnHMIStatus *hmiStatus = (SDLOnHMIStatus *)notification.notification; - - if (hmiStatus.windowID != nil && hmiStatus.windowID.integerValue != SDLPredefinedWindowsDefaultWindow) { - return; - } - - if (self.isFirstHMILevelFull || ![hmiStatus.hmiLevel isEqualToEnum:SDLHMILevelFull]) { - return; - } - - self.isFirstHMILevelFull = YES; - [self sdl_subscribeToSystemCapabilityUpdates]; + [self sdl_saveSystemCapability:systemCapabilityResponse.systemCapability error:nil completionHandler:nil]; } @end diff --git a/SmartDeviceLink/SDLSystemCapabilityObserver.h b/SmartDeviceLink/SDLSystemCapabilityObserver.h index 37819c6dc..f45f3869d 100644 --- a/SmartDeviceLink/SDLSystemCapabilityObserver.h +++ b/SmartDeviceLink/SDLSystemCapabilityObserver.h @@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN typedef void (^SDLCapabilityUpdateHandler)(SDLSystemCapability *capability); -typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability *capability, NSError *error); +typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability *_Nullable capability, BOOL subscribed, NSError *_Nullable error); /** An observer object for SDLSystemCapabilityManager @@ -34,7 +34,10 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability *capabil /** A block called when the observer is triggered */ -@property (copy, nonatomic) SDLCapabilityUpdateHandler block; +@property (copy, nonatomic) SDLCapabilityUpdateHandler block __deprecated_msg("use updateBlock instead"); + +/// A block called when the observer is triggered +@property (copy, nonatomic) SDLCapabilityUpdateWithErrorHandler updateBlock; /** Create an observer using an object and a selector on that object @@ -52,7 +55,7 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability *capabil @param block The block that will be called when the subscription triggers @return The observer */ -- (instancetype)initWithObserver:(id)observer block:(SDLCapabilityUpdateHandler)block; +- (instancetype)initWithObserver:(id)observer block:(SDLCapabilityUpdateHandler)block __deprecated_msg("use initWithObserver:updateHandler: instead"); /// Create an observer using an object and a callback block diff --git a/SmartDeviceLink/SDLSystemCapabilityObserver.m b/SmartDeviceLink/SDLSystemCapabilityObserver.m index 2b782540a..7b6ef0b0e 100644 --- a/SmartDeviceLink/SDLSystemCapabilityObserver.m +++ b/SmartDeviceLink/SDLSystemCapabilityObserver.m @@ -32,12 +32,12 @@ - (instancetype)initWithObserver:(id)observer block:(SDLCapabilityUpda return self; } -- (instancetype)initWithObserver:(id)observer block:(SDLCapabilityUpdateWithErrorHandler)block { +- (instancetype)initWithObserver:(id)observer updateHandler:(SDLCapabilityUpdateWithErrorHandler)block { self = [super init]; if (!self) { return nil; } _observer = observer; - _block = block; + _updateBlock = block; return self; } From eb9e9db2c93e4cdd9f2575ffecb676059a3c5e94 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 31 Jan 2020 15:44:54 -0500 Subject: [PATCH 05/39] Remove unused code --- SmartDeviceLink/SDLSystemCapabilityManager.m | 1 - 1 file changed, 1 deletion(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 5cedbdefe..e2d591422 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -608,7 +608,6 @@ - (void)sdl_registerForNotifications { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_displayLayoutResponse:) name:SDLDidReceiveSetDisplayLayoutResponse object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_systemCapabilityUpdatedNotification:) name:SDLDidReceiveSystemCapabilityUpdatedNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_systemCapabilityResponseNotification:) name:SDLDidReceiveGetSystemCapabilitiesResponse object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_hmiStatusNotification:) name:SDLDidChangeHMIStatusNotification object:nil]; } /** From f51add1e2c8b17ccef0805833e3b4f7ddddb15e2 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 31 Jan 2020 16:14:19 -0500 Subject: [PATCH 06/39] Send subscription status in handlers * Update subscription status dict --- SmartDeviceLink/SDLSystemCapabilityManager.m | 35 ++++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index e2d591422..20f6a3515 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -340,13 +340,14 @@ - (void)updateCapabilityType:(SDLSystemCapabilityType)type completionHandler:(SD /// Sends a GetSystemCapability and sends back the response /// @param type The type to get -/// @param subscribe Whether to change the subscription status +/// @param subscribe Whether to change the subscription status. YES to subscribe, NO to unsubscribe, nil to keep whatever the current state is /// @param handler The handler to be returned - (void)sdl_sendGetSystemCapabilityWithType:(SDLSystemCapabilityType)type subscribe:(nullable NSNumber *)subscribe completionHandler:(nullable SDLCapabilityUpdateWithErrorHandler)handler { __weak typeof(self) weakSelf = self; SDLGetSystemCapability *getSystemCapability = [[SDLGetSystemCapability alloc] initWithType:type]; getSystemCapability.subscribe = subscribe; + __weak typeof(self) weakself = self; [self.connectionManager sendConnectionRequest:getSystemCapability withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { if (![response isKindOfClass:[SDLGetSystemCapabilityResponse class]]) { if (handler == nil) { return; } @@ -362,8 +363,10 @@ - (void)sdl_sendGetSystemCapabilityWithType:(SDLSystemCapabilityType)type subscr } SDLGetSystemCapabilityResponse *getSystemCapabilityResponse = (SDLGetSystemCapabilityResponse *)response; + if (![weakself.subscriptionStatus[type] isEqualToNumber:subscribe] && weakself.supportsSubscriptions) { + weakself.subscriptionStatus[type] = subscribe; + } - // TODO: Keep track of subscriptions [weakSelf sdl_saveSystemCapability:getSystemCapabilityResponse.systemCapability error:error completionHandler:handler]; }]; } @@ -493,6 +496,7 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n #pragma mark - Manager Subscriptions - (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withBlock:(SDLCapabilityUpdateHandler)block { + // TODO: If it doesn't support subscriptions, get the data only once, then return NO // DISPLAYS always works due to old-style SetDisplayLayoutRepsonse updates, but otherwise, subscriptions won't work if (!self.supportsSubscriptions && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { return nil; } @@ -501,7 +505,9 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n if (self.capabilityObservers[type] == nil) { self.capabilityObservers[type] = [NSMutableArray array]; - // TODO: Subscribe + if (self.supportsSubscriptions) { + [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:nil]; + } } [self.capabilityObservers[type] addObject:observerObject]; @@ -509,6 +515,7 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n } - (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withUpdateBlock:(SDLCapabilityUpdateWithErrorHandler)block { + // TODO: If it doesn't support subscriptions, get the data only once, then return NO // DISPLAYS always works due to old-style SetDisplayLayoutRepsonse updates, but otherwise, subscriptions won't work if (!self.supportsSubscriptions && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { return nil; } @@ -517,7 +524,9 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n if (self.capabilityObservers[type] == nil) { self.capabilityObservers[type] = [NSMutableArray array]; - // TODO: Subscribe + if (self.supportsSubscriptions) { + [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:nil]; + } } [self.capabilityObservers[type] addObject:observerObject]; @@ -532,14 +541,14 @@ - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id // DISPLAYS always works due to old-style SetDisplayLayoutResponse updates, but otherwise, subscriptions won't work if (!self.supportsSubscriptions && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { return NO; } - // TODO: If the first subscription to the type, subscribe to it in `GetSystemCapability` - SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:observer selector:selector]; if (self.capabilityObservers[type] == nil) { self.capabilityObservers[type] = [NSMutableArray array]; - // TODO: Subscribe + if (self.supportsSubscriptions) { + [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:nil]; + } } [self.capabilityObservers[type] addObject:observerObject]; @@ -550,7 +559,11 @@ - (void)unsubscribeFromCapabilityType:(SDLSystemCapabilityType)type withObserver for (SDLSystemCapabilityObserver *capabilityObserver in self.capabilityObservers[type]) { if ([observer isEqual:capabilityObserver.observer]) { [self.capabilityObservers[type] removeObject:capabilityObserver]; - // TODO: If this was the last observer, unsubscribe from the HU + + if (self.capabilityObservers[type].count == 0 && self.supportsSubscriptions) { + [self sdl_sendGetSystemCapabilityWithType:type subscribe:@NO completionHandler:nil]; + } + break; } } @@ -567,7 +580,7 @@ - (void)sdl_callObserversForType:(SDLSystemCapabilityType)type update:(nullable observer.block(capability); #pragma clang diagnostic pop } else if (observer.updateBlock != nil) { - observer.updateBlock(capability, NO, error); // TODO: Subscribed based on whether we are subscribed + observer.updateBlock(capability, self.subscriptionStatus[type].boolValue, error); } else { if (![observer respondsToSelector:observer.selector]) { @throw [NSException sdl_invalidSelectorExceptionWithSelector:observer.selector]; @@ -585,7 +598,7 @@ - (void)sdl_callObserversForType:(SDLSystemCapabilityType)type update:(nullable [invocation setArgument:&error atIndex:3]; } if (numberOfParametersInSelector >= 3) { - BOOL argVal = NO; // TODO: Send actual subscription status + BOOL argVal = self.subscriptionStatus[type].boolValue; [invocation setArgument:&argVal atIndex:4]; } if (numberOfParametersInSelector >= 4) { @@ -597,7 +610,7 @@ - (void)sdl_callObserversForType:(SDLSystemCapabilityType)type update:(nullable } if (handler == nil) { return; } - handler(capability, NO, nil); // TODO: Subscribed or not needs to say YES if we're subscribed + handler(capability, self.subscriptionStatus[type].boolValue, nil); } #pragma mark - Notifications From 3c957f0c5329b4139348ec772f7af7e169b803c3 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 4 Feb 2020 14:02:14 -0500 Subject: [PATCH 07/39] Numerous updates for subscriptions * Added logs --- SmartDeviceLink/SDLSystemCapabilityManager.h | 54 +++-- SmartDeviceLink/SDLSystemCapabilityManager.m | 215 +++++++++++-------- 2 files changed, 155 insertions(+), 114 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.h b/SmartDeviceLink/SDLSystemCapabilityManager.h index bfd96a0bd..4465cf177 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.h +++ b/SmartDeviceLink/SDLSystemCapabilityManager.h @@ -248,12 +248,22 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla - (void)stop; /** - * Retrieves a capability type from the remote system. This function must be called in order to retrieve the values for `navigationCapability`, `phoneCapability`, `videoStreamingCapability`, `remoteControlCapability`, and `appServicesCapabilities`. If you do not call this method first, those values will be nil. After calling this method, assuming there is no error in the handler, you may retrieve the capability you requested from the manager within the handler. + * Returns the window capability object of the primary display with the specified window ID. This is a convenient method to easily access capabilities of windows for instance widget windows of the main display. + * + * @param windowID The ID of the window to get capabilities + * @returns The window capability object representing the window capabilities of the window with the specified window ID or nil if the window is not known or no window capabilities exist. + */ +- (nullable SDLWindowCapability *)windowCapabilityWithWindowID:(NSUInteger)windowID; + +/** + * This method has been superceded by `subscribeToCapabilityType:` methods. You should use one of those instead, unless you only want a value once, and it must be updated. If you subscribe to a capability and are connected to a head unit that does not support subscriptions, when this method returns, it will also call all subscriptions. Therefore, you can use this method to force an update to all subscriptions of that particular type. + * + * Retrieves and updates a capability type from the remote system. This function must be called in order to retrieve the values for `navigationCapability`, `phoneCapability`, `videoStreamingCapability`, `remoteControlCapability`, and `appServicesCapabilities`. If you do not call this method first, those values will be nil. After calling this method, assuming there is no error in the handler, you may retrieve the capability you requested from the manager within the handler. * * @param type The type of capability to retrieve * @param handler The handler to be called when the retrieval is complete */ -- (void)updateCapabilityType:(SDLSystemCapabilityType)type completionHandler:(SDLUpdateCapabilityHandler)handler __deprecated_msg("use a subscribe method instead, which will return a single new value if subscriptions are not available"); +- (void)updateCapabilityType:(SDLSystemCapabilityType)type completionHandler:(SDLUpdateCapabilityHandler)handler; /// Returns whether or not the capability type is supported on the system. You can use this to check if subscribing to the capability will work. @@ -262,22 +272,30 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla - (BOOL)isCapabilitySupported:(SDLSystemCapabilityType)type; /// Subscribe to a particular capability type using a block callback. - -/// This method will be called immediately with the current value and called every time the value is updated on RPC v5.1.0+ systems (`supportsSubscriptions == YES`). If this method is called on a sub-v5.1.0 system (`supportsSubscriptions == NO`), the method will return `NO` and the selector will never be called, unless the `type` is `DISPLAYS` which is supported on every version. - +/// +/// On v5.1.0+ systems (where `supportsSubscriptions == YES`): +/// This method will be called immediately with the current value and will be called every time the value is updated. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and a subscription will be attempted. The current cached value (`nil`) will nevertheless be returned immediately. +/// +/// On sub-v5.1.0 systems (where `supportsSubscriptions == NO`): +/// The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and returned. The current cached value (`nil`) will nevertheless be returned immediately. +/// /// @param type The type of capability to subscribe to /// @param block The block to be called when the capability is updated /// @return An object that can be used to unsubscribe the block using unsubscribeFromCapabilityType:withObserver: by passing it in the observer callback, or nil if subscriptions aren't available on this head unit - (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withBlock:(SDLCapabilityUpdateHandler)block __deprecated_msg("use subscribeToCapabilityType:withUpdateBlock: instead"); -/// Subscribe to a particular capability type using a block callback. - -/// This method will be called immediately with the current value and called every time the value is updated on RPC v5.1.0+ systems (`supportsSubscriptions == YES`). If this method is called on a sub-v5.1.0 system (`supportsSubscriptions == NO`), the method will return `NO` and the selector will never be called, unless the `type` is `DISPLAYS` which is supported on every version. +/// Subscribe to a particular capability type using a handler callback. +/// +/// On v5.1.0+ systems (where `supportsSubscriptions == YES`): +/// This method will be called immediately with the current value and will be called every time the value is updated. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and a subscription will be attempted. The current cached value (`nil`) will nevertheless be returned immediately. +/// +/// On sub-v5.1.0 systems (where `supportsSubscriptions == NO`): +/// The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and returned. The current cached value (`nil`) will nevertheless be returned immediately. /// @param type The type of capability to subscribe to -/// @param block The block to be called when the capability is updated with an error if one occurs +/// @param handler The block to be called when the capability is updated with an error if one occurs /// @return An object that can be used to unsubscribe the block using unsubscribeFromCapabilityType:withObserver: by passing it in the observer callback, or nil if subscriptions aren't available on this head unit -- (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withUpdateBlock:(SDLCapabilityUpdateWithErrorHandler)block; +- (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withUpdateHandler:(SDLCapabilityUpdateWithErrorHandler)handler; /** * Subscribe to a particular capability type with a selector callback. @@ -292,7 +310,11 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla * * 4. Three parameters, one `SDLSystemCapability *` parameter, one `NSError` parameter, and one `BOOL` parameter e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability error:(NSError *)error subscribed:(BOOL)subscribed` * - * This method will be called immediately with the current value and called every time the value is updated on RPC v5.1.0+ systems (`supportsSubscriptions == YES`). If this method is called on a sub-v5.1.0 system (`supportsSubscriptions == NO`), the method will return `NO` and the selector will never be called, unless the `type` is `DISPLAYS` which is supported on every version. + * On v5.1.0+ systems (where `supportsSubscriptions == YES`): + This method will be called immediately with the current value and will be called every time the value is updated. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and a subscription will be attempted. The current cached value (`nil`) will nevertheless be returned immediately. + * + * On sub-v5.1.0 systems (where `supportsSubscriptions == NO`): + * The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and returned. The current cached value (`nil`) will nevertheless be returned immediately. * * @param type The type of the system capability to subscribe to * @param observer The object that will have `selector` called whenever the capability is updated @@ -302,21 +324,13 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id)observer selector:(SEL)selector; /** - * Unsubscribe from a particular capability type. If it was subscribed with a block, the return value should be passed to the `observer` to unsubscribe the block. If it was subscribed with a selector, the `observer` object should be passed to unsubscribe the object selector. + * Unsubscribe from a particular capability type. If it was subscribed with a block / handler, the return value should be passed to the `observer` to unsubscribe the block. If it was subscribed with a selector, the `observer` object (on which the selector exists and is called) should be passed to unsubscribe the object selector. * * @param type The type of the system capability to unsubscribe from * @param observer The object that will be unsubscribed. If a block was subscribed, the return value should be passed. If a selector was subscribed, the observer object should be passed. */ - (void)unsubscribeFromCapabilityType:(SDLSystemCapabilityType)type withObserver:(id)observer; -/** - * Returns the window capability object of the primary display with the specified window ID. This is a convenient method to easily access capabilities of windows for instance widget windows of the main display. - * - * @param windowID The ID of the window to get capabilities - * @returns The window capability object representing the window capabilities of the window with the specified window ID or nil if the window is not known or no window capabilities exist. - */ -- (nullable SDLWindowCapability *)windowCapabilityWithWindowID:(NSUInteger)windowID; - @end NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 20f6a3515..a003c161b 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -187,6 +187,7 @@ - (nullable SDLWindowCapability *)defaultMainWindowCapability { /// @param buttons The old-style `SDLButtonCapabilities` object to convert /// @param softButtons The old-style `SDLSoftButtonCapabilities` to convert - (NSArray *)sdl_createDisplayCapabilityListFromDeprecatedDisplayCapabilities:(SDLDisplayCapabilities *)display buttons:(NSArray *)buttons softButtons:(NSArray *)softButtons { + SDLLogV(@"Creating display capability from deprecated display capabilities"); // Based on deprecated Display capabilities we don't know if widgets are supported. The default MAIN window is the only window we know is supported, so it's the only one we will expose. SDLWindowTypeCapabilities *windowTypeCapabilities = [[SDLWindowTypeCapabilities alloc] initWithType:SDLWindowTypeMain maximumNumberOfWindows:1]; #pragma clang diagnostic push @@ -231,35 +232,29 @@ For backward compatibility (AppLink 2.0) static image type is always presented #pragma mark Convert New to Deprecated -/// Convert from a WindowCapability (should be the main display's main window capability) to the deprecated old-style DisplayCapabilities -/// @param displayName The display name of the display to be converted -/// @param windowCapability The window capability to be converted -- (SDLDisplayCapabilities *)sdl_createDeprecatedDisplayCapabilitiesWithDisplayName:(NSString *)displayName windowCapability:(SDLWindowCapability *)windowCapability { - SDLDisplayCapabilities *convertedCapabilities = [[SDLDisplayCapabilities alloc] init]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated" - convertedCapabilities.displayType = SDLDisplayTypeGeneric; // deprecated but it is mandatory -#pragma clang diagnostic pop - convertedCapabilities.displayName = displayName; - convertedCapabilities.textFields = [windowCapability.textFields copy]; - convertedCapabilities.imageFields = [windowCapability.imageFields copy]; - convertedCapabilities.templatesAvailable = [windowCapability.templatesAvailable copy]; - convertedCapabilities.numCustomPresetsAvailable = [windowCapability.numCustomPresetsAvailable copy]; - convertedCapabilities.mediaClockFormats = @[]; // mandatory field but allows empty array - convertedCapabilities.graphicSupported = @([windowCapability.imageTypeSupported containsObject:SDLImageTypeDynamic]); - - return convertedCapabilities; -} - /// Update the internal deprecated display capability methods with new values based on the current value of the default main window capability and the primary display - (void)sdl_updateDeprecatedDisplayCapabilities { + SDLLogV(@"Updating deprecated capabilities from default main window capabilities"); SDLWindowCapability *defaultMainWindowCapabilities = self.defaultMainWindowCapability; if (self.displays.count == 0) { return; } // Create the deprecated capabilities for backward compatibility if developers try to access them - self.displayCapabilities = [self sdl_createDeprecatedDisplayCapabilitiesWithDisplayName:self.displays.firstObject.displayName windowCapability:defaultMainWindowCapabilities]; + SDLDisplayCapabilities *convertedCapabilities = [[SDLDisplayCapabilities alloc] init]; + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated" + convertedCapabilities.displayType = SDLDisplayTypeGeneric; // deprecated but it is mandatory + #pragma clang diagnostic pop + convertedCapabilities.displayName = self.displays.firstObject.displayName; + convertedCapabilities.textFields = [defaultMainWindowCapabilities.textFields copy]; + convertedCapabilities.imageFields = [defaultMainWindowCapabilities.imageFields copy]; + convertedCapabilities.templatesAvailable = [defaultMainWindowCapabilities.templatesAvailable copy]; + convertedCapabilities.numCustomPresetsAvailable = [defaultMainWindowCapabilities.numCustomPresetsAvailable copy]; + convertedCapabilities.mediaClockFormats = @[]; // mandatory field but allows empty array + convertedCapabilities.graphicSupported = @([defaultMainWindowCapabilities.imageTypeSupported containsObject:SDLImageTypeDynamic]); + + self.displayCapabilities = convertedCapabilities; self.buttonCapabilities = defaultMainWindowCapabilities.buttonCapabilities; self.softButtonCapabilities = defaultMainWindowCapabilities.softButtonCapabilities; } @@ -290,7 +285,7 @@ - (BOOL)isCapabilitySupported:(SDLSystemCapabilityType)type { } else if ([type isEqualToEnum:SDLSystemCapabilityTypeVideoStreaming]) { if ([[SDLGlobals sharedGlobals].rpcVersion isGreaterThanOrEqualToVersion:[SDLVersion versionWithString:@"3.0.0"]] && [[SDLGlobals sharedGlobals].rpcVersion isLessThanOrEqualToVersion:[SDLVersion versionWithString:@"4.4.0"]]) { // This was before the system capability feature was added so check if graphics are supported instead using the deprecated display capabilities - return self.displayCapabilities.graphicSupported; + return self.displayCapabilities.graphicSupported.boolValue; } return self.hmiCapabilities.videoStreaming.boolValue; @@ -324,6 +319,7 @@ - (nullable SDLSystemCapability *)sdl_cachedCapabilityForType:(SDLSystemCapabili } - (void)updateCapabilityType:(SDLSystemCapabilityType)type completionHandler:(SDLUpdateCapabilityHandler)handler { + SDLLogV(@"Updating capability type: %@", type); if (self.supportsSubscriptions) { // Just return the cached data because we get `onSystemCapability` callbacks handler(nil, self); @@ -343,6 +339,7 @@ - (void)updateCapabilityType:(SDLSystemCapabilityType)type completionHandler:(SD /// @param subscribe Whether to change the subscription status. YES to subscribe, NO to unsubscribe, nil to keep whatever the current state is /// @param handler The handler to be returned - (void)sdl_sendGetSystemCapabilityWithType:(SDLSystemCapabilityType)type subscribe:(nullable NSNumber *)subscribe completionHandler:(nullable SDLCapabilityUpdateWithErrorHandler)handler { + SDLLogV(@"Sending GetSystemCapability with type: %@, subscribe: %@", type, subscribe); __weak typeof(self) weakSelf = self; SDLGetSystemCapability *getSystemCapability = [[SDLGetSystemCapability alloc] initWithType:type]; getSystemCapability.subscribe = subscribe; @@ -350,12 +347,14 @@ - (void)sdl_sendGetSystemCapabilityWithType:(SDLSystemCapabilityType)type subscr __weak typeof(self) weakself = self; [self.connectionManager sendConnectionRequest:getSystemCapability withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { if (![response isKindOfClass:[SDLGetSystemCapabilityResponse class]]) { + SDLLogE(@"GetSystemCapability failed, type: %@, did not return a GetSystemCapability response", type); if (handler == nil) { return; } handler(nil, NO, error); return; } if (error != nil) { + SDLLogE(@"GetSystemCapability failed, type: %@, error: %@", type, error); // An error is returned if the request was unsuccessful or if a Generic Response was returned if (handler == nil) { return; } handler(nil, NO, error); @@ -363,6 +362,8 @@ - (void)sdl_sendGetSystemCapabilityWithType:(SDLSystemCapabilityType)type subscr } SDLGetSystemCapabilityResponse *getSystemCapabilityResponse = (SDLGetSystemCapabilityResponse *)response; + SDLLogD(@"GetSystemCapability response succeeded, type: %@, response: %@", type, getSystemCapabilityResponse); + if (![weakself.subscriptionStatus[type] isEqualToNumber:subscribe] && weakself.supportsSubscriptions) { weakself.subscriptionStatus[type] = subscribe; } @@ -381,8 +382,9 @@ - (void)sdl_sendGetSystemCapabilityWithType:(SDLSystemCapabilityType)type subscr @return Whether or not the save occurred. This can be `NO` if the new system capability is equivalent to the old capability. */ - (BOOL)sdl_saveSystemCapability:(nullable SDLSystemCapability *)systemCapability error:(nullable NSError *)error completionHandler:(nullable SDLCapabilityUpdateWithErrorHandler)handler { + SDLLogV(@"Saving system capability type: %@", systemCapability); if ([self.lastReceivedCapability isEqual:systemCapability]) { - [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:error handler:handler]; + [self sdl_callObserversForUpdate:systemCapability error:error handler:handler]; return NO; } self.lastReceivedCapability = systemCapability; @@ -391,31 +393,31 @@ - (BOOL)sdl_saveSystemCapability:(nullable SDLSystemCapability *)systemCapabilit if ([systemCapabilityType isEqualToEnum:SDLSystemCapabilityTypePhoneCall]) { if ([self.phoneCapability isEqual:systemCapability.phoneCapability]) { - [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:error handler:handler]; + [self sdl_callObserversForUpdate:systemCapability error:error handler:handler]; return NO; } self.phoneCapability = systemCapability.phoneCapability; } else if ([systemCapabilityType isEqualToEnum:SDLSystemCapabilityTypeNavigation]) { if ([self.navigationCapability isEqual:systemCapability.navigationCapability]) { - [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:error handler:handler]; + [self sdl_callObserversForUpdate:systemCapability error:error handler:handler]; return NO; } self.navigationCapability = systemCapability.navigationCapability; } else if ([systemCapabilityType isEqualToEnum:SDLSystemCapabilityTypeRemoteControl]) { if ([self.remoteControlCapability isEqual:systemCapability.remoteControlCapability]) { - [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:error handler:handler]; + [self sdl_callObserversForUpdate:systemCapability error:error handler:handler]; return NO; } self.remoteControlCapability = systemCapability.remoteControlCapability; } else if ([systemCapabilityType isEqualToEnum:SDLSystemCapabilityTypeSeatLocation]) { if ([self.seatLocationCapability isEqual:systemCapability.seatLocationCapability]) { - [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:error handler:handler]; + [self sdl_callObserversForUpdate:systemCapability error:error handler:handler]; return NO; } self.seatLocationCapability = systemCapability.seatLocationCapability; } else if ([systemCapabilityType isEqualToEnum:SDLSystemCapabilityTypeVideoStreaming]) { if ([self.videoStreamingCapability isEqual:systemCapability.videoStreamingCapability]) { - [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:error handler:handler]; + [self sdl_callObserversForUpdate:systemCapability error:error handler:handler]; return NO; } self.videoStreamingCapability = systemCapability.videoStreamingCapability; @@ -432,13 +434,14 @@ - (BOOL)sdl_saveSystemCapability:(nullable SDLSystemCapability *)systemCapabilit SDLLogD(@"Updated system capability manager with new data: %@", systemCapability); - [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:error handler:handler]; + [self sdl_callObserversForUpdate:systemCapability error:error handler:handler]; return YES; } #pragma mark Merge Capability Deltas - (void)sdl_saveAppServicesCapabilitiesUpdate:(SDLAppServicesCapabilities *)newCapabilities { + SDLLogV(@"Saving app services capability update with new capabilities: %@", newCapabilities); for (SDLAppServiceCapability *capability in newCapabilities.appServices) { if (capability.updateReason == nil) { // First update, new capability @@ -456,6 +459,7 @@ - (void)sdl_saveAppServicesCapabilitiesUpdate:(SDLAppServicesCapabilities *)newC /// @param newCapabilities The new `DisplayCapability` update delta. - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)newCapabilities { NSArray *oldCapabilities = self.displays; + SDLLogV(@"Saving display capability update with new capabilities: %@", newCapabilities); if (oldCapabilities == nil) { self.displays = newCapabilities; @@ -475,15 +479,18 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n SDLWindowCapability *oldWindow = copyWindowCapabilities[i]; NSUInteger newWindowID = newWindow.windowID ? newWindow.windowID.unsignedIntegerValue : SDLPredefinedWindowsDefaultWindow; NSUInteger oldWindowID = oldWindow.windowID ? oldWindow.windowID.unsignedIntegerValue : SDLPredefinedWindowsDefaultWindow; + if (newWindowID == oldWindowID) { - copyWindowCapabilities[i] = newWindow; // replace the old window caps with new ones + // Replace the old window caps with new ones + copyWindowCapabilities[i] = newWindow; oldFound = true; break; } } if (!oldFound) { - [copyWindowCapabilities addObject:newWindow]; // this is a new unknown window + // This is a new unknown window + [copyWindowCapabilities addObject:newWindow]; } } @@ -496,71 +503,76 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n #pragma mark - Manager Subscriptions - (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withBlock:(SDLCapabilityUpdateHandler)block { - // TODO: If it doesn't support subscriptions, get the data only once, then return NO - // DISPLAYS always works due to old-style SetDisplayLayoutRepsonse updates, but otherwise, subscriptions won't work - if (!self.supportsSubscriptions && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { return nil; } - + SDLLogD(@"Subscribing to capability type: %@ with a handler", type); SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:[[NSObject alloc] init] block:block]; 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 array]; - - if (self.supportsSubscriptions) { - [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:nil]; - } + [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:nil]; } [self.capabilityObservers[type] addObject:observerObject]; + // Call the block immediately with the cached value + [self sdl_invokeObserver:observerObject withCapability:[self sdl_cachedCapabilityForType:type] error:nil]; + return observerObject.observer; } -- (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withUpdateBlock:(SDLCapabilityUpdateWithErrorHandler)block { - // TODO: If it doesn't support subscriptions, get the data only once, then return NO - // DISPLAYS always works due to old-style SetDisplayLayoutRepsonse updates, but otherwise, subscriptions won't work - if (!self.supportsSubscriptions && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { return nil; } - - SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:[[NSObject alloc] init] updateHandler:block]; +- (nullable id)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.capabilityObservers[type] == nil) { - self.capabilityObservers[type] = [NSMutableArray array]; + SDLLogD(@"This is the first subscription to capability type: %@, sending a GetSystemCapability with subscribe true", type); - if (self.supportsSubscriptions) { - [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:nil]; - } + self.capabilityObservers[type] = [NSMutableArray array]; + [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:nil]; } [self.capabilityObservers[type] addObject:observerObject]; + // Call the block immediately with the cached value + [self sdl_invokeObserver:observerObject withCapability:[self sdl_cachedCapabilityForType:type] error:nil]; + return observerObject.observer; } - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id)observer selector:(SEL)selector { + SDLLogD(@"Subscribing to capability type: %@, with observer: %@, selector: %@", type, observer, NSStringFromSelector(selector)); NSUInteger numberOfParametersInSelector = [NSStringFromSelector(selector) componentsSeparatedByString:@":"].count - 1; - if (numberOfParametersInSelector > 2) { return NO; } - - // TODO: If it doesn't support subscriptions, get the data only once, then return NO - // DISPLAYS always works due to old-style SetDisplayLayoutResponse updates, but otherwise, subscriptions won't work - if (!self.supportsSubscriptions && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { return NO; } + if (numberOfParametersInSelector > 3) { + SDLLogE(@"Attempted to subscribe to a capability using a selector that contains more than 3 parameters."); + return NO; + } SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:observer selector:selector]; if (self.capabilityObservers[type] == nil) { - self.capabilityObservers[type] = [NSMutableArray array]; + SDLLogD(@"This is the first subscription to capability type: %@, sending a GetSystemCapability with subscribe true", type); - if (self.supportsSubscriptions) { - [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:nil]; - } + self.capabilityObservers[type] = [NSMutableArray array]; + [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:nil]; } [self.capabilityObservers[type] addObject:observerObject]; - return YES; + // Call immediately with the cached value + [self sdl_invokeObserver:observerObject withCapability:[self sdl_cachedCapabilityForType:type] error:nil]; + + // If it doesn't support subscriptions, get the data only once (subscribe can be YES, it will be ignored), then return NO. DISPLAYS always works due to old-style SetDisplayLayoutResponse updates, but otherwise, subscriptions won't work. + if (!self.supportsSubscriptions && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { return NO; + } else { + return YES; + } } - (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]) { + if ([observer isEqual:capabilityObserver.observer] && self.capabilityObservers[type] != nil) { [self.capabilityObservers[type] removeObject:capabilityObserver]; if (self.capabilityObservers[type].count == 0 && self.supportsSubscriptions) { + SDLLogD(@"Removing the last subscription to type %@, sending a GetSystemCapability with subscribe false (will unsubscribe)", type); [self sdl_sendGetSystemCapabilityWithType:type subscribe:@NO completionHandler:nil]; } @@ -572,51 +584,58 @@ - (void)unsubscribeFromCapabilityType:(SDLSystemCapabilityType)type withObserver /// 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 -- (void)sdl_callObserversForType:(SDLSystemCapabilityType)type update:(nullable SDLSystemCapability *)capability error:(nullable NSError *)error handler:(nullable SDLCapabilityUpdateWithErrorHandler)handler { +- (void)sdl_callObserversForUpdate:(nullable SDLSystemCapability *)capability error:(nullable NSError *)error handler:(nullable SDLCapabilityUpdateWithErrorHandler)handler { + SDLSystemCapabilityType type = capability.systemCapabilityType; + SDLLogV(@"Calling observers for type: %@ with update: %@", type, capability); for (SDLSystemCapabilityObserver *observer in self.capabilityObservers[type]) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - if (observer.block != nil) { - observer.block(capability); -#pragma clang diagnostic pop - } else if (observer.updateBlock != nil) { - observer.updateBlock(capability, self.subscriptionStatus[type].boolValue, error); - } else { - if (![observer respondsToSelector:observer.selector]) { - @throw [NSException sdl_invalidSelectorExceptionWithSelector:observer.selector]; - } - - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[(NSObject *)observer.observer methodSignatureForSelector:observer.selector]]; - [invocation setSelector:observer.selector]; - [invocation setTarget:observer.observer]; - - NSUInteger numberOfParametersInSelector = [NSStringFromSelector(observer.selector) componentsSeparatedByString:@":"].count - 1; - if (numberOfParametersInSelector >= 1) { - [invocation setArgument:&capability atIndex:2]; - } - if (numberOfParametersInSelector >= 2) { - [invocation setArgument:&error atIndex:3]; - } - if (numberOfParametersInSelector >= 3) { - BOOL argVal = self.subscriptionStatus[type].boolValue; - [invocation setArgument:&argVal atIndex:4]; - } - if (numberOfParametersInSelector >= 4) { - @throw [NSException sdl_invalidSelectorExceptionWithSelector:observer.selector]; - } - - [invocation invoke]; - } + [self sdl_invokeObserver:observer withCapability:capability error:error]; } if (handler == nil) { return; } handler(capability, self.subscriptionStatus[type].boolValue, nil); } +- (void)sdl_invokeObserver:(SDLSystemCapabilityObserver *)observer withCapability:(nullable SDLSystemCapability *)capability error:(nullable NSError *)error { + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + if (observer.block != nil) { + observer.block(capability); + #pragma clang diagnostic pop + } else if (observer.updateBlock != nil) { + observer.updateBlock(capability, self.subscriptionStatus[capability.systemCapabilityType].boolValue, error); + } else { + if (![observer respondsToSelector:observer.selector]) { + @throw [NSException sdl_invalidSelectorExceptionWithSelector:observer.selector]; + } + + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[(NSObject *)observer.observer methodSignatureForSelector:observer.selector]]; + [invocation setSelector:observer.selector]; + [invocation setTarget:observer.observer]; + + NSUInteger numberOfParametersInSelector = [NSStringFromSelector(observer.selector) componentsSeparatedByString:@":"].count - 1; + if (numberOfParametersInSelector >= 1) { + [invocation setArgument:&capability atIndex:2]; + } + if (numberOfParametersInSelector >= 2) { + [invocation setArgument:&error atIndex:3]; + } + if (numberOfParametersInSelector >= 3) { + BOOL argVal = self.subscriptionStatus[capability.systemCapabilityType].boolValue; + [invocation setArgument:&argVal atIndex:4]; + } + if (numberOfParametersInSelector >= 4) { + @throw [NSException sdl_invalidSelectorExceptionWithSelector:observer.selector]; + } + + [invocation invoke]; + } +} + #pragma mark - Notifications /// Registers for notifications and responses from Core - (void)sdl_registerForNotifications { + SDLLogV(@"Registering for notifications"); [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_registerResponse:) name:SDLDidReceiveRegisterAppInterfaceResponse object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_displayLayoutResponse:) name:SDLDidReceiveSetDisplayLayoutResponse object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_systemCapabilityUpdatedNotification:) name:SDLDidReceiveSystemCapabilityUpdatedNotification object:nil]; @@ -651,6 +670,8 @@ - (void)sdl_registerResponse:(SDLRPCResponseNotification *)notification { self.shouldConvertDeprecatedDisplayCapabilities = YES; self.displays = [self sdl_createDisplayCapabilityListFromRegisterResponse:response]; + SDLLogV(@"Received RegisterAppInterface response, filled out display and other capabilities"); + // Call the observers in case the new display capability list is created from deprecated types SDLSystemCapability *systemCapability = [[SDLSystemCapability alloc] initWithDisplayCapabilities:self.displays]; [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:nil handler:nil]; @@ -678,6 +699,8 @@ - (void)sdl_displayLayoutResponse:(SDLRPCResponseNotification *)notification { self.displays = [self sdl_createDisplayCapabilityListFromSetDisplayLayoutResponse:response]; + SDLLogV(@"Received SetDisplayLayout response, filled out display and other capabilities"); + // Call the observers in case the new display capability list is created from deprecated types SDLSystemCapability *systemCapability = [[SDLSystemCapability alloc] initWithDisplayCapabilities:self.displays]; [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:nil handler:nil]; @@ -691,6 +714,8 @@ - (void)sdl_displayLayoutResponse:(SDLRPCResponseNotification *)notification { */ - (void)sdl_systemCapabilityUpdatedNotification:(SDLRPCNotificationNotification *)notification { SDLOnSystemCapabilityUpdated *systemCapabilityUpdatedNotification = (SDLOnSystemCapabilityUpdated *)notification.notification; + SDLLogV(@"Received OnSystemCapability update for type %@", systemCapabilityUpdatedNotification.systemCapability.systemCapabilityType); + [self sdl_saveSystemCapability:systemCapabilityUpdatedNotification.systemCapability error:nil completionHandler:nil]; } @@ -701,6 +726,8 @@ - (void)sdl_systemCapabilityUpdatedNotification:(SDLRPCNotificationNotification */ - (void)sdl_systemCapabilityResponseNotification:(SDLRPCResponseNotification *)notification { SDLGetSystemCapabilityResponse *systemCapabilityResponse = (SDLGetSystemCapabilityResponse *)notification.response; + SDLLogV(@"Received GetSystemCapability response for type %@", systemCapabilityResponse.systemCapability.systemCapabilityType); + [self sdl_saveSystemCapability:systemCapabilityResponse.systemCapability error:nil completionHandler:nil]; } From 7962c0bc423fc9d8de94d98111cb799d1c506f6d Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 4 Feb 2020 15:56:26 -0500 Subject: [PATCH 08/39] Working on tests --- SmartDeviceLink/SDLError.m | 2 +- SmartDeviceLink/SDLSystemCapabilityManager.m | 70 +++++++++---------- .../TestSystemCapabilityObserver.h | 6 +- .../TestSystemCapabilityObserver.m | 12 +++- .../SDLSystemCapabilityManagerSpec.m | 27 +++---- 5 files changed, 65 insertions(+), 52 deletions(-) diff --git a/SmartDeviceLink/SDLError.m b/SmartDeviceLink/SDLError.m index efe518cb8..0b932d6ac 100644 --- a/SmartDeviceLink/SDLError.m +++ b/SmartDeviceLink/SDLError.m @@ -381,7 +381,7 @@ + (NSException *)sdl_invalidLockscreenSetupException { + (NSException *)sdl_invalidSelectorExceptionWithSelector:(SEL)selector { return [NSException exceptionWithName:@"com.sdl.systemCapabilityManager.selectorException" - reason:[NSString stringWithFormat:@"Capability observation selector: %@ does not match possible selectors, which must have either 0 or 1 parameters", NSStringFromSelector(selector)] + reason:[NSString stringWithFormat:@"Capability observation selector: %@ does not match possible selectors, which must have between 0 and 3 parameters", NSStringFromSelector(selector)] userInfo:nil]; } diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index a003c161b..6268a8202 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -596,39 +596,39 @@ - (void)sdl_callObserversForUpdate:(nullable SDLSystemCapability *)capability er } - (void)sdl_invokeObserver:(SDLSystemCapabilityObserver *)observer withCapability:(nullable SDLSystemCapability *)capability error:(nullable NSError *)error { - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wdeprecated-declarations" - if (observer.block != nil) { - observer.block(capability); - #pragma clang diagnostic pop - } else if (observer.updateBlock != nil) { - observer.updateBlock(capability, self.subscriptionStatus[capability.systemCapabilityType].boolValue, error); - } else { - if (![observer respondsToSelector:observer.selector]) { - @throw [NSException sdl_invalidSelectorExceptionWithSelector:observer.selector]; - } - - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[(NSObject *)observer.observer methodSignatureForSelector:observer.selector]]; - [invocation setSelector:observer.selector]; - [invocation setTarget:observer.observer]; - - NSUInteger numberOfParametersInSelector = [NSStringFromSelector(observer.selector) componentsSeparatedByString:@":"].count - 1; - if (numberOfParametersInSelector >= 1) { - [invocation setArgument:&capability atIndex:2]; - } - if (numberOfParametersInSelector >= 2) { - [invocation setArgument:&error atIndex:3]; - } - if (numberOfParametersInSelector >= 3) { - BOOL argVal = self.subscriptionStatus[capability.systemCapabilityType].boolValue; - [invocation setArgument:&argVal atIndex:4]; - } - if (numberOfParametersInSelector >= 4) { - @throw [NSException sdl_invalidSelectorExceptionWithSelector:observer.selector]; - } - - [invocation invoke]; - } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + if (observer.block != nil) { + observer.block(capability); +#pragma clang diagnostic pop + } else if (observer.updateBlock != nil) { + observer.updateBlock(capability, self.subscriptionStatus[capability.systemCapabilityType].boolValue, error); + } else { + if (![observer respondsToSelector:observer.selector]) { + @throw [NSException sdl_invalidSelectorExceptionWithSelector:observer.selector]; + } + + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[(NSObject *)observer.observer methodSignatureForSelector:observer.selector]]; + [invocation setSelector:observer.selector]; + [invocation setTarget:observer.observer]; + + NSUInteger numberOfParametersInSelector = [NSStringFromSelector(observer.selector) componentsSeparatedByString:@":"].count - 1; + if (numberOfParametersInSelector >= 1) { + [invocation setArgument:&capability atIndex:2]; + } + if (numberOfParametersInSelector >= 2) { + [invocation setArgument:&error atIndex:3]; + } + if (numberOfParametersInSelector >= 3) { + BOOL argVal = self.subscriptionStatus[capability.systemCapabilityType].boolValue; + [invocation setArgument:&argVal atIndex:4]; + } + if (numberOfParametersInSelector >= 4) { + @throw [NSException sdl_invalidSelectorExceptionWithSelector:observer.selector]; + } + + [invocation invoke]; + } } #pragma mark - Notifications @@ -674,7 +674,7 @@ - (void)sdl_registerResponse:(SDLRPCResponseNotification *)notification { // Call the observers in case the new display capability list is created from deprecated types SDLSystemCapability *systemCapability = [[SDLSystemCapability alloc] initWithDisplayCapabilities:self.displays]; - [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:nil handler:nil]; + [self sdl_callObserversForUpdate:systemCapability error:nil handler:nil]; } /** @@ -703,7 +703,7 @@ - (void)sdl_displayLayoutResponse:(SDLRPCResponseNotification *)notification { // Call the observers in case the new display capability list is created from deprecated types SDLSystemCapability *systemCapability = [[SDLSystemCapability alloc] initWithDisplayCapabilities:self.displays]; - [self sdl_callObserversForType:systemCapability.systemCapabilityType update:systemCapability error:nil handler:nil]; + [self sdl_callObserversForUpdate:systemCapability error:nil handler:nil]; } diff --git a/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.h b/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.h index 0e6c8bcbb..080662b7a 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.h +++ b/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.h @@ -8,7 +8,7 @@ #import -@class SDLSystemCapabilityManager; +@class SDLSystemCapability; NS_ASSUME_NONNULL_BEGIN @@ -17,7 +17,9 @@ NS_ASSUME_NONNULL_BEGIN @property (assign, nonatomic) NSUInteger selectorCalledCount; - (void)capabilityUpdated; -- (void)capabilityUpdatedWithNotification:(SDLSystemCapabilityManager *)capabilityManager; +- (void)capabilityUpdatedWithCapability:(SDLSystemCapability *)capability; +- (void)capabilityUpdatedWithCapability:(SDLSystemCapability *)capability error:(NSError *)error; +- (void)capabilityUpdatedWithCapability:(SDLSystemCapability *)capability error:(NSError *)error subscribed:(BOOL)subscribed; @end diff --git a/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.m b/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.m index 5366b0884..9169cb662 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.m +++ b/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.m @@ -8,6 +8,8 @@ #import "TestSystemCapabilityObserver.h" +#import "SDLSystemCapability.h" + @implementation TestSystemCapabilityObserver - (instancetype)init { @@ -23,7 +25,15 @@ - (void)capabilityUpdated { self.selectorCalledCount++; } -- (void)capabilityUpdatedWithNotification:(SDLSystemCapabilityManager *)capabilityManager { +- (void)capabilityUpdatedWithNotification:(SDLSystemCapability *)capability { + self.selectorCalledCount++; +} + +- (void)capabilityUpdatedWithCapability:(SDLSystemCapability *)capability error:(NSError *)error { + self.selectorCalledCount++; +} + +- (void)capabilityUpdatedWithCapability:(SDLSystemCapability *)capability error:(NSError *)error subscribed:(BOOL)subscribed { self.selectorCalledCount++; } diff --git a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m index e114328e0..7b6c7c000 100644 --- a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m +++ b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m @@ -11,6 +11,7 @@ #import "SDLDisplayCapability.h" #import "SDLGetSystemCapability.h" #import "SDLGetSystemCapabilityResponse.h" +#import "SDLGlobals.h" #import "SDLHMICapabilities.h" #import "SDLImageField.h" #import "SDLImageResolution.h" @@ -32,6 +33,7 @@ #import "SDLSystemCapability.h" #import "SDLSystemCapabilityManager.h" #import "SDLTextField.h" +#import "SDLVersion.h" #import "SDLVideoStreamingCapability.h" #import "SDLWindowCapability.h" #import "SDLWindowTypeCapabilities.h" @@ -41,8 +43,6 @@ @interface SDLSystemCapabilityManager () -@property (assign, nonatomic, readwrite) BOOL supportsSubscriptions; - @end @@ -452,15 +452,15 @@ @interface SDLSystemCapabilityManager () }); }); - describe(@"subscribing to capability types", ^{ + fdescribe(@"subscribing to capability types", ^{ __block TestSystemCapabilityObserver *phoneObserver = nil; __block TestSystemCapabilityObserver *navigationObserver = nil; - __block NSUInteger blockObserverTriggeredCount = 0; + __block NSUInteger observerTriggeredCount = 0; beforeEach(^{ - blockObserverTriggeredCount = 0; - testSystemCapabilityManager.supportsSubscriptions = YES; + observerTriggeredCount = 0; + [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"5.1.0"]; // supports subscriptions phoneObserver = [[TestSystemCapabilityObserver alloc] init]; [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withObserver:phoneObserver selector:@selector(capabilityUpdatedWithNotification:)]; @@ -472,7 +472,8 @@ @interface SDLSystemCapabilityManager () __block BOOL observationSuccess = NO; beforeEach(^{ - testSystemCapabilityManager.supportsSubscriptions = NO; + [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"5.0.0"]; // doesn't subscriptions + observationSuccess = [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withObserver:phoneObserver selector:@selector(capabilityUpdatedWithNotification:)]; }); @@ -487,7 +488,7 @@ @interface SDLSystemCapabilityManager () beforeEach(^{ blockObserver = [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withBlock:^(SDLSystemCapability * _Nonnull systemCapability) { - blockObserverTriggeredCount++; + observerTriggeredCount++; }]; SDLGetSystemCapabilityResponse *testResponse = [[SDLGetSystemCapabilityResponse alloc] init]; @@ -499,7 +500,7 @@ @interface SDLSystemCapabilityManager () it(@"should notify subscribers of the new data", ^{ expect(phoneObserver.selectorCalledCount).toEventually(equal(1)); - expect(blockObserverTriggeredCount).toEventually(equal(1)); + expect(observerTriggeredCount).toEventually(equal(1)); expect(navigationObserver.selectorCalledCount).toEventually(equal(0)); }); @@ -517,7 +518,7 @@ @interface SDLSystemCapabilityManager () it(@"should not notify the subscriber of the new data", ^{ expect(phoneObserver.selectorCalledCount).toEventually(equal(1)); // No change from above - expect(blockObserverTriggeredCount).toEventually(equal(1)); + expect(observerTriggeredCount).toEventually(equal(1)); expect(navigationObserver.selectorCalledCount).toEventually(equal(0)); }); }); @@ -528,7 +529,7 @@ @interface SDLSystemCapabilityManager () beforeEach(^{ blockObserver = [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withBlock:^(SDLSystemCapability * _Nonnull systemCapability) { - blockObserverTriggeredCount++; + observerTriggeredCount++; }]; SDLOnSystemCapabilityUpdated *testNotification = [[SDLOnSystemCapabilityUpdated alloc] initWithSystemCapability:[[SDLSystemCapability alloc] initWithPhoneCapability:[[SDLPhoneCapability alloc] initWithDialNumber:YES]]]; @@ -539,7 +540,7 @@ @interface SDLSystemCapabilityManager () it(@"should notify subscribers of the new data", ^{ expect(phoneObserver.selectorCalledCount).toEventually(equal(1)); - expect(blockObserverTriggeredCount).toEventually(equal(1)); + expect(observerTriggeredCount).toEventually(equal(1)); expect(navigationObserver.selectorCalledCount).toEventually(equal(0)); }); @@ -557,7 +558,7 @@ @interface SDLSystemCapabilityManager () it(@"should not notify the subscriber of the new data", ^{ expect(phoneObserver.selectorCalledCount).toEventually(equal(1)); // No change from above - expect(blockObserverTriggeredCount).toEventually(equal(1)); + expect(observerTriggeredCount).toEventually(equal(1)); expect(navigationObserver.selectorCalledCount).toEventually(equal(0)); }); }); From 5ec80f8465577cf3853dbff92dffe3060649c328 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 4 Feb 2020 16:02:50 -0500 Subject: [PATCH 09/39] Fix the proper observer / selector not being called --- SmartDeviceLink/SDLSystemCapabilityManager.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 6268a8202..cd771b6b0 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -604,7 +604,7 @@ - (void)sdl_invokeObserver:(SDLSystemCapabilityObserver *)observer withCapabilit } else if (observer.updateBlock != nil) { observer.updateBlock(capability, self.subscriptionStatus[capability.systemCapabilityType].boolValue, error); } else { - if (![observer respondsToSelector:observer.selector]) { + if (![observer.observer respondsToSelector:observer.selector]) { @throw [NSException sdl_invalidSelectorExceptionWithSelector:observer.selector]; } From 0017893e016dff3fecae1a9ae84ac8b4169ffec3 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 4 Feb 2020 16:51:18 -0500 Subject: [PATCH 10/39] Documentation and test fixes --- SmartDeviceLink/SDLSystemCapabilityManager.h | 15 +++++++-------- .../DevAPISpecs/TestSystemCapabilityObserver.m | 2 +- .../SDLSystemCapabilityManagerSpec.m | 18 ++++++++++++------ 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.h b/SmartDeviceLink/SDLSystemCapabilityManager.h index 4465cf177..2c9766542 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.h +++ b/SmartDeviceLink/SDLSystemCapabilityManager.h @@ -255,6 +255,11 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla */ - (nullable SDLWindowCapability *)windowCapabilityWithWindowID:(NSUInteger)windowID; +/// Returns whether or not the capability type is supported on the system. You can use this to check if subscribing to the capability will work. +/// @param type The SystemCapabilityType that will be checked. +/// @return Whether or not `type` is supported by the connected head unit. +- (BOOL)isCapabilitySupported:(SDLSystemCapabilityType)type; + /** * This method has been superceded by `subscribeToCapabilityType:` methods. You should use one of those instead, unless you only want a value once, and it must be updated. If you subscribe to a capability and are connected to a head unit that does not support subscriptions, when this method returns, it will also call all subscriptions. Therefore, you can use this method to force an update to all subscriptions of that particular type. * @@ -265,12 +270,6 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla */ - (void)updateCapabilityType:(SDLSystemCapabilityType)type completionHandler:(SDLUpdateCapabilityHandler)handler; - -/// Returns whether or not the capability type is supported on the system. You can use this to check if subscribing to the capability will work. -/// @param type The SystemCapabilityType that will be checked. -/// @return Whether or not `type` is supported by the connected head unit. -- (BOOL)isCapabilitySupported:(SDLSystemCapabilityType)type; - /// Subscribe to a particular capability type using a block callback. /// /// On v5.1.0+ systems (where `supportsSubscriptions == YES`): @@ -282,7 +281,7 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla /// @param type The type of capability to subscribe to /// @param block The block to be called when the capability is updated /// @return An object that can be used to unsubscribe the block using unsubscribeFromCapabilityType:withObserver: by passing it in the observer callback, or nil if subscriptions aren't available on this head unit -- (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withBlock:(SDLCapabilityUpdateHandler)block __deprecated_msg("use subscribeToCapabilityType:withUpdateBlock: instead"); +- (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withBlock:(SDLCapabilityUpdateHandler)block __deprecated_msg("use subscribeToCapabilityType:withUpdateHandler: instead"); /// Subscribe to a particular capability type using a handler callback. /// @@ -311,7 +310,7 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla * 4. Three parameters, one `SDLSystemCapability *` parameter, one `NSError` parameter, and one `BOOL` parameter e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability error:(NSError *)error subscribed:(BOOL)subscribed` * * On v5.1.0+ systems (where `supportsSubscriptions == YES`): - This method will be called immediately with the current value and will be called every time the value is updated. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and a subscription will be attempted. The current cached value (`nil`) will nevertheless be returned immediately. + * This method will be called immediately with the current value and will be called every time the value is updated. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and a subscription will be attempted. The current cached value (`nil`) will nevertheless be returned immediately. * * On sub-v5.1.0 systems (where `supportsSubscriptions == NO`): * The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and returned. The current cached value (`nil`) will nevertheless be returned immediately. diff --git a/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.m b/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.m index 9169cb662..1c1672848 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.m +++ b/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.m @@ -25,7 +25,7 @@ - (void)capabilityUpdated { self.selectorCalledCount++; } -- (void)capabilityUpdatedWithNotification:(SDLSystemCapability *)capability { +- (void)capabilityUpdatedWithCapability:(SDLSystemCapability *)capability { self.selectorCalledCount++; } diff --git a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m index 7b6c7c000..e8c9ad5a3 100644 --- a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m +++ b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m @@ -452,20 +452,21 @@ @interface SDLSystemCapabilityManager () }); }); - fdescribe(@"subscribing to capability types", ^{ + describe(@"subscribing to capability types", ^{ __block TestSystemCapabilityObserver *phoneObserver = nil; __block TestSystemCapabilityObserver *navigationObserver = nil; __block NSUInteger observerTriggeredCount = 0; + __block NSUInteger handlerTriggeredCount = 0; beforeEach(^{ observerTriggeredCount = 0; [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"5.1.0"]; // supports subscriptions phoneObserver = [[TestSystemCapabilityObserver alloc] init]; - [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withObserver:phoneObserver selector:@selector(capabilityUpdatedWithNotification:)]; + [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withObserver:phoneObserver selector:@selector(capabilityUpdatedWithCapability:)]; navigationObserver = [[TestSystemCapabilityObserver alloc] init]; - [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypeNavigation withObserver:navigationObserver selector:@selector(capabilityUpdatedWithNotification:)]; + [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypeNavigation withObserver:navigationObserver selector:@selector(capabilityUpdatedWithCapability:)]; }); describe(@"when observers aren't supported", ^{ @@ -474,8 +475,7 @@ @interface SDLSystemCapabilityManager () beforeEach(^{ [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"5.0.0"]; // doesn't subscriptions - - observationSuccess = [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withObserver:phoneObserver selector:@selector(capabilityUpdatedWithNotification:)]; + observationSuccess = [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withObserver:phoneObserver selector:@selector(capabilityUpdatedWithCapability:)]; }); it(@"should fail to subscribe", ^{ @@ -483,14 +483,19 @@ @interface SDLSystemCapabilityManager () }); }); - context(@"from a GetSystemCapabilitiesResponse", ^{ + fcontext(@"from a GetSystemCapabilitiesResponse", ^{ __block id blockObserver = nil; + __block id handlerObserver = nil; beforeEach(^{ blockObserver = [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withBlock:^(SDLSystemCapability * _Nonnull systemCapability) { observerTriggeredCount++; }]; + handlerObserver = [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withUpdateHandler:^(SDLSystemCapability * _Nullable capability, BOOL subscribed, NSError * _Nullable error) { + handlerTriggeredCount++; + }]; + SDLGetSystemCapabilityResponse *testResponse = [[SDLGetSystemCapabilityResponse alloc] init]; testResponse.systemCapability = [[SDLSystemCapability alloc] initWithPhoneCapability:[[SDLPhoneCapability alloc] initWithDialNumber:YES]]; SDLRPCResponseNotification *notification = [[SDLRPCResponseNotification alloc] initWithName:SDLDidReceiveGetSystemCapabilitiesResponse object:nil rpcResponse:testResponse]; @@ -500,6 +505,7 @@ @interface SDLSystemCapabilityManager () it(@"should notify subscribers of the new data", ^{ expect(phoneObserver.selectorCalledCount).toEventually(equal(1)); + expect(handlerTriggeredCount).toEventually(equal(1)); expect(observerTriggeredCount).toEventually(equal(1)); expect(navigationObserver.selectorCalledCount).toEventually(equal(0)); }); From d2b712764477306cc7ebdaa2a6cdc59c0515d3b4 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 5 Feb 2020 10:01:55 -0500 Subject: [PATCH 11/39] Fixing tests --- .../SDLSystemCapabilityManagerSpec.m | 47 +++++++------------ 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m index e8c9ad5a3..e613d4757 100644 --- a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m +++ b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m @@ -48,7 +48,7 @@ @interface SDLSystemCapabilityManager () QuickSpecBegin(SDLSystemCapabilityManagerSpec) -describe(@"System capability manager", ^{ +fdescribe(@"System capability manager", ^{ __block SDLSystemCapabilityManager *testSystemCapabilityManager = nil; __block TestConnectionManager *testConnectionManager = nil; @@ -461,6 +461,7 @@ @interface SDLSystemCapabilityManager () beforeEach(^{ observerTriggeredCount = 0; + handlerTriggeredCount = 0; [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"5.1.0"]; // supports subscriptions phoneObserver = [[TestSystemCapabilityObserver alloc] init]; @@ -483,7 +484,7 @@ @interface SDLSystemCapabilityManager () }); }); - fcontext(@"from a GetSystemCapabilitiesResponse", ^{ + context(@"from a GetSystemCapabilitiesResponse", ^{ __block id blockObserver = nil; __block id handlerObserver = nil; @@ -504,15 +505,16 @@ @interface SDLSystemCapabilityManager () }); it(@"should notify subscribers of the new data", ^{ - expect(phoneObserver.selectorCalledCount).toEventually(equal(1)); - expect(handlerTriggeredCount).toEventually(equal(1)); - expect(observerTriggeredCount).toEventually(equal(1)); - expect(navigationObserver.selectorCalledCount).toEventually(equal(0)); + expect(phoneObserver.selectorCalledCount).toEventually(equal(2)); + expect(handlerTriggeredCount).toEventually(equal(2)); + expect(observerTriggeredCount).toEventually(equal(2)); + expect(navigationObserver.selectorCalledCount).toEventually(equal(1)); }); describe(@"unsubscribing", ^{ beforeEach(^{ [testSystemCapabilityManager unsubscribeFromCapabilityType:SDLSystemCapabilityTypePhoneCall withObserver:phoneObserver]; + [testSystemCapabilityManager unsubscribeFromCapabilityType:SDLSystemCapabilityTypePhoneCall withObserver:handlerObserver]; [testSystemCapabilityManager unsubscribeFromCapabilityType:SDLSystemCapabilityTypePhoneCall withObserver:blockObserver]; SDLGetSystemCapabilityResponse *testResponse = [[SDLGetSystemCapabilityResponse alloc] init]; @@ -523,9 +525,10 @@ @interface SDLSystemCapabilityManager () }); it(@"should not notify the subscriber of the new data", ^{ - expect(phoneObserver.selectorCalledCount).toEventually(equal(1)); // No change from above - expect(observerTriggeredCount).toEventually(equal(1)); - expect(navigationObserver.selectorCalledCount).toEventually(equal(0)); + expect(phoneObserver.selectorCalledCount).toEventually(equal(2)); // No change from above + expect(handlerTriggeredCount).toEventually(equal(2)); + expect(observerTriggeredCount).toEventually(equal(2)); + expect(navigationObserver.selectorCalledCount).toEventually(equal(1)); }); }); }); @@ -545,9 +548,9 @@ @interface SDLSystemCapabilityManager () }); it(@"should notify subscribers of the new data", ^{ - expect(phoneObserver.selectorCalledCount).toEventually(equal(1)); - expect(observerTriggeredCount).toEventually(equal(1)); - expect(navigationObserver.selectorCalledCount).toEventually(equal(0)); + expect(phoneObserver.selectorCalledCount).toEventually(equal(2)); + expect(observerTriggeredCount).toEventually(equal(2)); + expect(navigationObserver.selectorCalledCount).toEventually(equal(1)); }); describe(@"unsubscribing", ^{ @@ -563,9 +566,9 @@ @interface SDLSystemCapabilityManager () }); it(@"should not notify the subscriber of the new data", ^{ - expect(phoneObserver.selectorCalledCount).toEventually(equal(1)); // No change from above - expect(observerTriggeredCount).toEventually(equal(1)); - expect(navigationObserver.selectorCalledCount).toEventually(equal(0)); + expect(phoneObserver.selectorCalledCount).toEventually(equal(2)); // No change from above + expect(observerTriggeredCount).toEventually(equal(2)); + expect(navigationObserver.selectorCalledCount).toEventually(equal(1)); }); }); }); @@ -632,20 +635,6 @@ @interface SDLSystemCapabilityManager () }); }); - describe(@"when entering HMI FULL", ^{ - beforeEach(^{ - SDLOnHMIStatus *fullStatus = [[SDLOnHMIStatus alloc] init]; - fullStatus.hmiLevel = SDLHMILevelFull; - SDLRPCNotificationNotification *notification = [[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:fullStatus]; - [[NSNotificationCenter defaultCenter] postNotification:notification]; - }); - - it(@"should send GetSystemCapability subscriptions for all known capabilities", ^{ - expect(testConnectionManager.receivedRequests).to(haveCount(7)); - expect(testConnectionManager.receivedRequests.lastObject).to(beAnInstanceOf([SDLGetSystemCapability class])); - }); - }); - describe(@"when the system capability manager is stopped after being started", ^{ beforeEach(^{ [testSystemCapabilityManager stop]; From 281a711b40409547f2c1779e1dba24d1765659e4 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 5 Feb 2020 12:01:38 -0500 Subject: [PATCH 12/39] Add tests for isCapabilitySupported --- SmartDeviceLink/SDLSystemCapabilityManager.m | 50 +++-- .../SDLSystemCapabilityManagerSpec.m | 179 +++++++++++++++++- 2 files changed, 202 insertions(+), 27 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index cd771b6b0..8079b6a7b 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -264,34 +264,32 @@ - (void)sdl_updateDeprecatedDisplayCapabilities { - (BOOL)isCapabilitySupported:(SDLSystemCapabilityType)type { if ([self sdl_cachedCapabilityForType:type] != nil) { return YES; - } else if (self.hmiCapabilities != nil) { - if ([type isEqualToEnum:SDLSystemCapabilityTypePhoneCall]) { - return self.hmiCapabilities.phoneCall.boolValue; - } else if ([type isEqualToEnum:SDLSystemCapabilityTypeNavigation]) { - return self.hmiCapabilities.navigation.boolValue; - } else if ([type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { - return self.hmiCapabilities.displays.boolValue; - } else if ([type isEqualToEnum:SDLSystemCapabilityTypeRemoteControl]) { - return self.hmiCapabilities.remoteControl.boolValue; - } else if ([type isEqualToEnum:SDLSystemCapabilityTypeSeatLocation]) { - return self.hmiCapabilities.seatLocation.boolValue; - } else if ([type isEqualToEnum:SDLSystemCapabilityTypeAppServices]) { - //This is a corner case that the param was not available in 5.1.0, but the app services feature was available. We have to say it's available because we don't know. - if ([[SDLGlobals sharedGlobals].rpcVersion isEqualToVersion:[SDLVersion versionWithString:@"5.1.0"]]) { - return YES; - } - - return self.hmiCapabilities.appServices.boolValue; - } else if ([type isEqualToEnum:SDLSystemCapabilityTypeVideoStreaming]) { - if ([[SDLGlobals sharedGlobals].rpcVersion isGreaterThanOrEqualToVersion:[SDLVersion versionWithString:@"3.0.0"]] && [[SDLGlobals sharedGlobals].rpcVersion isLessThanOrEqualToVersion:[SDLVersion versionWithString:@"4.4.0"]]) { - // This was before the system capability feature was added so check if graphics are supported instead using the deprecated display capabilities - return self.displayCapabilities.graphicSupported.boolValue; - } + } else if ([type isEqualToEnum:SDLSystemCapabilityTypePhoneCall]) { + return self.hmiCapabilities.phoneCall.boolValue; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeNavigation]) { + return self.hmiCapabilities.navigation.boolValue; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { + return self.hmiCapabilities.displays.boolValue; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeRemoteControl]) { + return self.hmiCapabilities.remoteControl.boolValue; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeSeatLocation]) { + return self.hmiCapabilities.seatLocation.boolValue; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeAppServices]) { + //This is a corner case that the param was not available in 5.1.0, but the app services feature was available. We have to say it's available because we don't know. + if ([[SDLGlobals sharedGlobals].rpcVersion isEqualToVersion:[SDLVersion versionWithString:@"5.1.0"]]) { + return YES; + } - return self.hmiCapabilities.videoStreaming.boolValue; - } else { - return NO; + return self.hmiCapabilities.appServices.boolValue; + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeVideoStreaming]) { + if ([[SDLGlobals sharedGlobals].rpcVersion isGreaterThanOrEqualToVersion:[SDLVersion versionWithString:@"3.0.0"]] && [[SDLGlobals sharedGlobals].rpcVersion isLessThanOrEqualToVersion:[SDLVersion versionWithString:@"4.4.0"]]) { + // This was before the system capability feature was added so check if graphics are supported instead using the deprecated display capabilities + return self.displayCapabilities.graphicSupported.boolValue; } + + return self.hmiCapabilities.videoStreaming.boolValue; + } else { + return NO; } diff --git a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m index e613d4757..48a4824cf 100644 --- a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m +++ b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m @@ -43,12 +43,30 @@ @interface SDLSystemCapabilityManager () +@property (nullable, strong, nonatomic, readwrite) NSArray *displays; +@property (nullable, strong, nonatomic, readwrite) SDLDisplayCapabilities *displayCapabilities; +@property (nullable, strong, nonatomic, readwrite) SDLHMICapabilities *hmiCapabilities; +@property (nullable, copy, nonatomic, readwrite) NSArray *softButtonCapabilities; +@property (nullable, copy, nonatomic, readwrite) NSArray *buttonCapabilities; +@property (nullable, strong, nonatomic, readwrite) SDLPresetBankCapabilities *presetBankCapabilities; +@property (nullable, copy, nonatomic, readwrite) NSArray *hmiZoneCapabilities; +@property (nullable, copy, nonatomic, readwrite) NSArray *speechCapabilities; +@property (nullable, copy, nonatomic, readwrite) NSArray *prerecordedSpeechCapabilities; +@property (nonatomic, assign, readwrite) BOOL vrCapability; +@property (nullable, copy, nonatomic, readwrite) NSArray *audioPassThruCapabilities; +@property (nullable, strong, nonatomic, readwrite) SDLAudioPassThruCapabilities *pcmStreamCapability; +@property (nullable, strong, nonatomic, readwrite) SDLNavigationCapability *navigationCapability; +@property (nullable, strong, nonatomic, readwrite) SDLPhoneCapability *phoneCapability; +@property (nullable, strong, nonatomic, readwrite) SDLVideoStreamingCapability *videoStreamingCapability; +@property (nullable, strong, nonatomic, readwrite) SDLRemoteControlCapabilities *remoteControlCapability; +@property (nullable, strong, nonatomic, readwrite) SDLSeatLocationCapability *seatLocationCapability; + @end QuickSpecBegin(SDLSystemCapabilityManagerSpec) -fdescribe(@"System capability manager", ^{ +describe(@"System capability manager", ^{ __block SDLSystemCapabilityManager *testSystemCapabilityManager = nil; __block TestConnectionManager *testConnectionManager = nil; @@ -142,6 +160,153 @@ @interface SDLSystemCapabilityManager () }); + describe(@"isCapabilitySupported method should work correctly", ^{ + __block SDLHMICapabilities *hmiCapabilities = nil; + + beforeEach(^{ + hmiCapabilities = [[SDLHMICapabilities alloc] init]; + }); + + context(@"when there's a cached phone capability and hmiCapabilities is nil", ^{ + beforeEach(^{ + testSystemCapabilityManager.phoneCapability = [[SDLPhoneCapability alloc] initWithDialNumber:YES]; + }); + + it(@"should return true", ^{ + expect([testSystemCapabilityManager isCapabilitySupported:SDLSystemCapabilityTypePhoneCall]).to(beTrue()); + }); + }); + + context(@"when there's no cached capability", ^{ + describe(@"pulling a phone capability when HMICapabilites.phoneCapability is false", ^{ + beforeEach(^{ + hmiCapabilities.phoneCall = @NO; + testSystemCapabilityManager.hmiCapabilities = hmiCapabilities; + }); + + it(@"should return NO", ^{ + expect([testSystemCapabilityManager isCapabilitySupported:SDLSystemCapabilityTypePhoneCall]).to(beFalse()); + }); + }); + + describe(@"pulling a phone capability when HMICapabilites.phoneCapability is true", ^{ + beforeEach(^{ + hmiCapabilities.phoneCall = @YES; + testSystemCapabilityManager.hmiCapabilities = hmiCapabilities; + }); + + it(@"should return NO", ^{ + expect([testSystemCapabilityManager isCapabilitySupported:SDLSystemCapabilityTypePhoneCall]).to(beTrue()); + }); + }); + + describe(@"pulling a phone capability when HMICapabilites.phoneCapability is nil", ^{ + it(@"should return NO", ^{ + expect([testSystemCapabilityManager isCapabilitySupported:SDLSystemCapabilityTypePhoneCall]).to(beFalse()); + }); + }); + + describe(@"pulling an app services capability", ^{ + context(@"on RPC connection version 5.1.0 and HMICapabilities.appServices is false", ^{ + beforeEach(^{ + [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"5.1.0"]; + hmiCapabilities.appServices = @NO; + testSystemCapabilityManager.hmiCapabilities = hmiCapabilities; + }); + + it(@"should return true", ^{ + expect([testSystemCapabilityManager isCapabilitySupported:SDLSystemCapabilityTypeAppServices]).to(beTrue()); + }); + }); + + context(@"on RPC connection version 5.2.0 and HMICapabilities.appServices is false", ^{ + beforeEach(^{ + [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"5.2.0"]; + hmiCapabilities.appServices = @NO; + testSystemCapabilityManager.hmiCapabilities = hmiCapabilities; + }); + + it(@"should return false", ^{ + expect([testSystemCapabilityManager isCapabilitySupported:SDLSystemCapabilityTypeAppServices]).to(beFalse()); + }); + }); + }); + + describe(@"pulling a video streaming capability", ^{ + context(@"on RPC connection version 2.0.0 and HMICapabilities is nil", ^{ + beforeEach(^{ + [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"2.0.0"]; + }); + + it(@"should return false", ^{ + expect([testSystemCapabilityManager isCapabilitySupported:SDLSystemCapabilityTypeVideoStreaming]).to(beFalse()); + }); + }); + + context(@"on RPC connection version 3.0.0 and HMICapabilities is nil", ^{ + beforeEach(^{ + [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"3.0.0"]; + }); + + context(@"when displayCapabilities.graphicSupported is true", ^{ + beforeEach(^{ + testSystemCapabilityManager.displayCapabilities = [[SDLDisplayCapabilities alloc] init]; + testSystemCapabilityManager.displayCapabilities.graphicSupported = @YES; + }); + + fit(@"should return true", ^{ + expect([testSystemCapabilityManager isCapabilitySupported:SDLSystemCapabilityTypeVideoStreaming]).to(beTrue()); + }); + }); + + context(@"when displayCapabilities.graphicSupported is false", ^{ + beforeEach(^{ + testSystemCapabilityManager.displayCapabilities.graphicSupported = @NO; + }); + + it(@"should return true", ^{ + expect([testSystemCapabilityManager isCapabilitySupported:SDLSystemCapabilityTypeVideoStreaming]).to(beFalse()); + }); + }); + }); + + context(@"on RPC connection version 5.1.0", ^{ + beforeEach(^{ + [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"5.1.0"]; + }); + + context(@"when HMICapabilites.videoStreaming is false", ^{ + beforeEach(^{ + hmiCapabilities.videoStreaming = @NO; + testSystemCapabilityManager.hmiCapabilities = hmiCapabilities; + }); + + it(@"should return false", ^{ + expect([testSystemCapabilityManager isCapabilitySupported:SDLSystemCapabilityTypeVideoStreaming]).to(beFalse()); + }); + }); + + context(@"when HMICapabilites.videoStreaming is true", ^{ + beforeEach(^{ + hmiCapabilities.videoStreaming = @YES; + testSystemCapabilityManager.hmiCapabilities = hmiCapabilities; + }); + + it(@"should return true", ^{ + expect([testSystemCapabilityManager isCapabilitySupported:SDLSystemCapabilityTypeVideoStreaming]).to(beTrue()); + }); + }); + + context(@"when HMICapabilites.videoStreaming is nil", ^{ + it(@"should return false", ^{ + expect([testSystemCapabilityManager isCapabilitySupported:SDLSystemCapabilityTypeVideoStreaming]).to(beFalse()); + }); + }); + }); + }); + }); + }); + context(@"When notified of a register app interface response", ^{ __block SDLRegisterAppInterfaceResponse *testRegisterAppInterfaceResponse = nil; __block SDLHMICapabilities *testHMICapabilities = nil; @@ -489,9 +654,12 @@ @interface SDLSystemCapabilityManager () __block id handlerObserver = nil; beforeEach(^{ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" blockObserver = [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withBlock:^(SDLSystemCapability * _Nonnull systemCapability) { observerTriggeredCount++; }]; +#pragma clang diagnostic pop handlerObserver = [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withUpdateHandler:^(SDLSystemCapability * _Nullable capability, BOOL subscribed, NSError * _Nullable error) { handlerTriggeredCount++; @@ -535,11 +703,19 @@ @interface SDLSystemCapabilityManager () context(@"from an OnSystemCapabilities notification", ^{ __block id blockObserver = nil; + __block id handlerObserver = nil; beforeEach(^{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" blockObserver = [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withBlock:^(SDLSystemCapability * _Nonnull systemCapability) { observerTriggeredCount++; }]; +#pragma clang diagnostic pop + + handlerObserver = [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withUpdateHandler:^(SDLSystemCapability * _Nullable capability, BOOL subscribed, NSError * _Nullable error) { + handlerTriggeredCount++; + }]; SDLOnSystemCapabilityUpdated *testNotification = [[SDLOnSystemCapabilityUpdated alloc] initWithSystemCapability:[[SDLSystemCapability alloc] initWithPhoneCapability:[[SDLPhoneCapability alloc] initWithDialNumber:YES]]]; SDLRPCNotificationNotification *notification = [[SDLRPCNotificationNotification alloc] initWithName:SDLDidReceiveSystemCapabilityUpdatedNotification object:nil rpcNotification:testNotification]; @@ -549,6 +725,7 @@ @interface SDLSystemCapabilityManager () it(@"should notify subscribers of the new data", ^{ expect(phoneObserver.selectorCalledCount).toEventually(equal(2)); + expect(handlerTriggeredCount).toEventually(equal(2)); expect(observerTriggeredCount).toEventually(equal(2)); expect(navigationObserver.selectorCalledCount).toEventually(equal(1)); }); From ede38868a3c959a24b299f1293a5a1910825882b Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 5 Feb 2020 12:10:05 -0500 Subject: [PATCH 13/39] Fixing documentation --- SmartDeviceLink/SDLSystemCapabilityManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.h b/SmartDeviceLink/SDLSystemCapabilityManager.h index 2c9766542..b190e7704 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.h +++ b/SmartDeviceLink/SDLSystemCapabilityManager.h @@ -255,7 +255,7 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla */ - (nullable SDLWindowCapability *)windowCapabilityWithWindowID:(NSUInteger)windowID; -/// Returns whether or not the capability type is supported on the system. You can use this to check if subscribing to the capability will work. +/// Returns whether or not the capability type is supported on the system. You can use this to check if subscribing to the capability will work. If this returns NO, then the feature is not supported by the head unit. If YES, the feature is supported by the head unit. You can subscribe to the capability type to get more information about the capability's support and features on the connected module. /// @param type The SystemCapabilityType that will be checked. /// @return Whether or not `type` is supported by the connected head unit. - (BOOL)isCapabilitySupported:(SDLSystemCapabilityType)type; From 22ac57a674872c5eb5ed66cae45cd9d3823eb030 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 5 Feb 2020 13:46:21 -0500 Subject: [PATCH 14/39] Fix GetSystemCapability should not be sent for DISPLAYS --- Example Apps/Example ObjC/ProxyManager.m | 9 +++ .../SmartDeviceLink-Example-ObjC.xcscheme | 78 +++++++++++++++++++ SmartDeviceLink/SDLSystemCapabilityManager.m | 24 ++++-- 3 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-ObjC.xcscheme diff --git a/Example Apps/Example ObjC/ProxyManager.m b/Example Apps/Example ObjC/ProxyManager.m index 35e28c9b0..8b2009974 100644 --- a/Example Apps/Example ObjC/ProxyManager.m +++ b/Example Apps/Example ObjC/ProxyManager.m @@ -161,6 +161,15 @@ - (void)sdlex_createMenus { - (void)sdlex_showInitialData { if (![self.sdlManager.hmiLevel isEqualToEnum:SDLHMILevelFull]) { return; } + SDLSystemCapabilityManager *scm = self.sdlManager.systemCapabilityManager; + id observerObj = [scm subscribeToCapabilityType:SDLSystemCapabilityTypeDisplays withUpdateHandler:^(SDLSystemCapability * _Nullable capability, BOOL subscribed, NSError * _Nullable error) { + NSLog(@"SCM update: %@, %@, %@", capability, (subscribed ? @"YES" : @"NO"), error); + }]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [scm unsubscribeFromCapabilityType:SDLSystemCapabilityTypeDisplays withObserver:observerObj]; + }); + SDLSetDisplayLayout *setDisplayLayout = [[SDLSetDisplayLayout alloc] initWithPredefinedLayout:SDLPredefinedLayoutNonMedia]; [self.sdlManager sendRequest:setDisplayLayout]; diff --git a/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-ObjC.xcscheme b/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-ObjC.xcscheme new file mode 100644 index 000000000..4d9f2d40f --- /dev/null +++ b/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-ObjC.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 8079b6a7b..a910ef62d 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -338,7 +338,6 @@ - (void)updateCapabilityType:(SDLSystemCapabilityType)type completionHandler:(SD /// @param handler The handler to be returned - (void)sdl_sendGetSystemCapabilityWithType:(SDLSystemCapabilityType)type subscribe:(nullable NSNumber *)subscribe completionHandler:(nullable SDLCapabilityUpdateWithErrorHandler)handler { SDLLogV(@"Sending GetSystemCapability with type: %@, subscribe: %@", type, subscribe); - __weak typeof(self) weakSelf = self; SDLGetSystemCapability *getSystemCapability = [[SDLGetSystemCapability alloc] initWithType:type]; getSystemCapability.subscribe = subscribe; @@ -366,7 +365,7 @@ - (void)sdl_sendGetSystemCapabilityWithType:(SDLSystemCapabilityType)type subscr weakself.subscriptionStatus[type] = subscribe; } - [weakSelf sdl_saveSystemCapability:getSystemCapabilityResponse.systemCapability error:error completionHandler:handler]; + [weakself sdl_saveSystemCapability:getSystemCapabilityResponse.systemCapability error:error completionHandler:handler]; }]; } @@ -507,7 +506,11 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n 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 array]; - [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:nil]; + + // 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]; + } } [self.capabilityObservers[type] addObject:observerObject]; @@ -523,9 +526,12 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n 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 array]; - [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:nil]; + + // 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]; + } } [self.capabilityObservers[type] addObject:observerObject]; @@ -547,9 +553,12 @@ - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id 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 array]; - [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:nil]; + + // 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]; + } } [self.capabilityObservers[type] addObject:observerObject]; @@ -571,6 +580,7 @@ - (void)unsubscribeFromCapabilityType:(SDLSystemCapabilityType)type withObserver if (self.capabilityObservers[type].count == 0 && self.supportsSubscriptions) { SDLLogD(@"Removing the last subscription to type %@, sending a GetSystemCapability with subscribe false (will unsubscribe)", type); + self.capabilityObservers[type] = nil; [self sdl_sendGetSystemCapabilityWithType:type subscribe:@NO completionHandler:nil]; } From a1b0b5d42ed74112770f3cb5d33eef91549200d6 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 5 Feb 2020 13:46:41 -0500 Subject: [PATCH 15/39] Remove test code --- Example Apps/Example ObjC/ProxyManager.m | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Example Apps/Example ObjC/ProxyManager.m b/Example Apps/Example ObjC/ProxyManager.m index 8b2009974..35e28c9b0 100644 --- a/Example Apps/Example ObjC/ProxyManager.m +++ b/Example Apps/Example ObjC/ProxyManager.m @@ -161,15 +161,6 @@ - (void)sdlex_createMenus { - (void)sdlex_showInitialData { if (![self.sdlManager.hmiLevel isEqualToEnum:SDLHMILevelFull]) { return; } - SDLSystemCapabilityManager *scm = self.sdlManager.systemCapabilityManager; - id observerObj = [scm subscribeToCapabilityType:SDLSystemCapabilityTypeDisplays withUpdateHandler:^(SDLSystemCapability * _Nullable capability, BOOL subscribed, NSError * _Nullable error) { - NSLog(@"SCM update: %@, %@, %@", capability, (subscribed ? @"YES" : @"NO"), error); - }]; - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [scm unsubscribeFromCapabilityType:SDLSystemCapabilityTypeDisplays withObserver:observerObj]; - }); - SDLSetDisplayLayout *setDisplayLayout = [[SDLSetDisplayLayout alloc] initWithPredefinedLayout:SDLPredefinedLayoutNonMedia]; [self.sdlManager sendRequest:setDisplayLayout]; From d8f54a6fa5631ab2a24637698a06ca3d3e3e05ed Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 5 Feb 2020 13:48:59 -0500 Subject: [PATCH 16/39] Don't allow unsubscribing from DISPLAYS --- SmartDeviceLink/SDLSystemCapabilityManager.m | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index a910ef62d..742b9b3ff 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -506,7 +506,7 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n 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 array]; - + // 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]; @@ -581,7 +581,11 @@ - (void)unsubscribeFromCapabilityType:(SDLSystemCapabilityType)type withObserver if (self.capabilityObservers[type].count == 0 && self.supportsSubscriptions) { SDLLogD(@"Removing the last subscription to type %@, sending a GetSystemCapability with subscribe false (will unsubscribe)", type); self.capabilityObservers[type] = nil; - [self sdl_sendGetSystemCapabilityWithType:type subscribe:@NO completionHandler:nil]; + + // 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:@NO completionHandler:nil]; + } } break; From 39efb439d8b5e62f716289ff3ea14e74db68a9ac Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 5 Feb 2020 16:28:26 -0500 Subject: [PATCH 17/39] Don't call observers twice with the same data * Fix swift names of APIs --- SmartDeviceLink/SDLSystemCapabilityManager.h | 14 +++++++------- SmartDeviceLink/SDLSystemCapabilityManager.m | 3 ++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.h b/SmartDeviceLink/SDLSystemCapabilityManager.h index b190e7704..87ad32864 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.h +++ b/SmartDeviceLink/SDLSystemCapabilityManager.h @@ -64,11 +64,6 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla */ @interface SDLSystemCapabilityManager : NSObject -/** - YES if subscriptions are available on the connected head unit. If NO, calls to `subscribeToCapabilityType:withBlock` and `subscribeToCapabilityType:withObserver:selector` will fail. - */ -@property (assign, nonatomic, readonly) BOOL supportsSubscriptions; - /** * Provides window capabilities of all displays connected with SDL. By default, one display is connected and supported which includes window capability information of the default main window of the display. May be nil if the system has not provided display and window capability information yet. * @@ -221,6 +216,11 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla */ @property (nullable, strong, nonatomic, readonly) SDLWindowCapability *defaultMainWindowCapability; +/** + YES if subscriptions are available on the connected module and you will automatically be notified if the value changes on the module. If NO, calls to `subscribe` methods will subscribe to updates, but the module will not automatically notify you. You will need to call `updateWithCapabilityType:completionHandler:` to force an update if you need one (though this should be rare). + */ +@property (assign, nonatomic, readonly) BOOL supportsSubscriptions; + /** Init is unavailable. Dependencies must be injected using initWithConnectionManager: @@ -258,7 +258,7 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla /// Returns whether or not the capability type is supported on the system. You can use this to check if subscribing to the capability will work. If this returns NO, then the feature is not supported by the head unit. If YES, the feature is supported by the head unit. You can subscribe to the capability type to get more information about the capability's support and features on the connected module. /// @param type The SystemCapabilityType that will be checked. /// @return Whether or not `type` is supported by the connected head unit. -- (BOOL)isCapabilitySupported:(SDLSystemCapabilityType)type; +- (BOOL)isCapabilitySupported:(SDLSystemCapabilityType)type NS_SWIFT_NAME(isCapabilitySupported(type:)); /** * This method has been superceded by `subscribeToCapabilityType:` methods. You should use one of those instead, unless you only want a value once, and it must be updated. If you subscribe to a capability and are connected to a head unit that does not support subscriptions, when this method returns, it will also call all subscriptions. Therefore, you can use this method to force an update to all subscriptions of that particular type. @@ -294,7 +294,7 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla /// @param type The type of capability to subscribe to /// @param handler The block to be called when the capability is updated with an error if one occurs /// @return An object that can be used to unsubscribe the block using unsubscribeFromCapabilityType:withObserver: by passing it in the observer callback, or nil if subscriptions aren't available on this head unit -- (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withUpdateHandler:(SDLCapabilityUpdateWithErrorHandler)handler; +- (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withUpdateHandler:(SDLCapabilityUpdateWithErrorHandler)handler NS_SWIFT_NAME(subscribe(capabilityType:updateHandler:)); /** * Subscribe to a particular capability type with a selector callback. diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 742b9b3ff..01af983d7 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -380,8 +380,9 @@ - (void)sdl_sendGetSystemCapabilityWithType:(SDLSystemCapabilityType)type subscr */ - (BOOL)sdl_saveSystemCapability:(nullable SDLSystemCapability *)systemCapability error:(nullable NSError *)error completionHandler:(nullable SDLCapabilityUpdateWithErrorHandler)handler { SDLLogV(@"Saving system capability type: %@", systemCapability); + + // If this is equal to the last received capability (e.g. a notification and GetSystemCapabilityResponse), don't save twice or call the observer twice. if ([self.lastReceivedCapability isEqual:systemCapability]) { - [self sdl_callObserversForUpdate:systemCapability error:error handler:handler]; return NO; } self.lastReceivedCapability = systemCapability; From 64c09d5d9c76f51a48ae3aa9322840f1ad1893eb Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 5 Feb 2020 16:45:05 -0500 Subject: [PATCH 18/39] Invoke observers if the initial GetSystemCapability fails with the error --- SmartDeviceLink/SDLError.h | 5 ++++ SmartDeviceLink/SDLError.m | 7 ++++++ SmartDeviceLink/SDLErrorConstants.h | 4 ++++ SmartDeviceLink/SDLSystemCapabilityManager.m | 24 ++++++++++++++++---- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/SmartDeviceLink/SDLError.h b/SmartDeviceLink/SDLError.h index 89cf49208..035919a1b 100644 --- a/SmartDeviceLink/SDLError.h +++ b/SmartDeviceLink/SDLError.h @@ -23,6 +23,7 @@ extern SDLErrorDomain *const SDLErrorDomainTextAndGraphicManager; extern SDLErrorDomain *const SDLErrorDomainSoftButtonManager; extern SDLErrorDomain *const SDLErrorDomainMenuManager; extern SDLErrorDomain *const SDLErrorDomainChoiceSetManager; +extern SDLErrorDomain *const SDLErrorDomainSystemCapabilityManager; extern SDLErrorDomain *const SDLErrorDomainTransport; extern SDLErrorDomain *const SDLErrorDomainRPCStore; @@ -86,6 +87,10 @@ extern SDLErrorDomain *const SDLErrorDomainRPCStore; + (NSError *)sdl_rpcStore_invalidObjectErrorWithObject:(id)wrongObject expectedType:(Class)type; +#pragma mark System Capability Manager + ++ (NSError *)sdl_systemCapabilityManager_moduleDoesNotSupportCapabilityType; + @end @interface NSException (SDLExceptions) diff --git a/SmartDeviceLink/SDLError.m b/SmartDeviceLink/SDLError.m index 0b932d6ac..4dca1bddb 100644 --- a/SmartDeviceLink/SDLError.m +++ b/SmartDeviceLink/SDLError.m @@ -21,6 +21,7 @@ SDLErrorDomain *const SDLErrorDomainSoftButtonManager = @"com.sdl.softbuttonmanager.error"; SDLErrorDomain *const SDLErrorDomainMenuManager = @"com.sdl.menumanager.error"; SDLErrorDomain *const SDLErrorDomainChoiceSetManager = @"com.sdl.choicesetmanager.error"; +SDLErrorDomain *const SDLErrorDomainSystemCapabilityManager = @"com.sdl.systemcapabilitymanager.error"; SDLErrorDomain *const SDLErrorDomainTransport = @"com.sdl.transport.error"; SDLErrorDomain *const SDLErrorDomainRPCStore = @"com.sdl.rpcStore.error"; @@ -288,6 +289,12 @@ + (NSError *)sdl_choiceSetManager_incorrectState:(SDLChoiceManagerState *)state return [NSError errorWithDomain:SDLErrorDomainChoiceSetManager code:SDLChoiceSetManagerErrorInvalidState userInfo:userInfo]; } +#pragma mark System Capability Manager + ++ (NSError *)sdl_systemCapabilityManager_moduleDoesNotSupportCapabilityType { + return [NSError errorWithDomain:SDLErrorDomainSystemCapabilityManager code:SDLSystemCapabilityManagerErrorModuleDoesNotSupportCapabilityType userInfo:nil]; +} + #pragma mark Transport + (NSError *)sdl_transport_unknownError { diff --git a/SmartDeviceLink/SDLErrorConstants.h b/SmartDeviceLink/SDLErrorConstants.h index f6c7e27ab..012b45cfe 100644 --- a/SmartDeviceLink/SDLErrorConstants.h +++ b/SmartDeviceLink/SDLErrorConstants.h @@ -152,6 +152,10 @@ typedef NS_ENUM(NSInteger, SDLChoiceSetManagerError) { SDLChoiceSetManagerErrorInvalidState = -5 }; +typedef NS_ENUM(NSInteger, SDLSystemCapabilityManagerError) { + SDLSystemCapabilityManagerErrorModuleDoesNotSupportCapabilityType = -1 +}; + /** * Errors associated with transport. */ diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 01af983d7..69643f63f 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -346,7 +346,7 @@ - (void)sdl_sendGetSystemCapabilityWithType:(SDLSystemCapabilityType)type subscr if (![response isKindOfClass:[SDLGetSystemCapabilityResponse class]]) { SDLLogE(@"GetSystemCapability failed, type: %@, did not return a GetSystemCapability response", type); if (handler == nil) { return; } - handler(nil, NO, error); + handler(nil, NO, [NSError sdl_systemCapabilityManager_moduleDoesNotSupportCapabilityType]); return; } @@ -510,7 +510,12 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n // 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]; + __weak typeof(self) weakself = self; + [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:^(SDLSystemCapability * _Nullable capability, BOOL subscribed, NSError * _Nullable error) { + if (error != nil) { + [weakself sdl_invokeObserver:observerObject withCapability:capability error:error]; + } + }]; } } [self.capabilityObservers[type] addObject:observerObject]; @@ -531,7 +536,12 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n // 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]; + __weak typeof(self) weakself = self; + [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:^(SDLSystemCapability * _Nullable capability, BOOL subscribed, NSError * _Nullable error) { + if (error != nil) { + [weakself sdl_invokeObserver:observerObject withCapability:capability error:error]; + } + }]; } } [self.capabilityObservers[type] addObject:observerObject]; @@ -551,14 +561,18 @@ - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id } SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:observer selector:selector]; - 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 array]; // 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]; + __weak typeof(self) weakself = self; + [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:^(SDLSystemCapability * _Nullable capability, BOOL subscribed, NSError * _Nullable error) { + if (error != nil) { + [weakself sdl_invokeObserver:observerObject withCapability:capability error:error]; + } + }]; } } [self.capabilityObservers[type] addObject:observerObject]; From 1763b37eac0877d0ac8930de5193528603aa4e0c Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 5 Feb 2020 16:48:11 -0500 Subject: [PATCH 19/39] Clarify the error message --- SmartDeviceLink/SDLError.h | 9 +++++---- SmartDeviceLink/SDLError.m | 9 +++++++-- SmartDeviceLink/SDLErrorConstants.h | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/SmartDeviceLink/SDLError.h b/SmartDeviceLink/SDLError.h index 035919a1b..06d647480 100644 --- a/SmartDeviceLink/SDLError.h +++ b/SmartDeviceLink/SDLError.h @@ -76,6 +76,11 @@ extern SDLErrorDomain *const SDLErrorDomainRPCStore; + (NSError *)sdl_choiceSetManager_failedToCreateMenuItems; + (NSError *)sdl_choiceSetManager_incorrectState:(NSString *)state; + +#pragma mark System Capability Manager + ++ (NSError *)sdl_systemCapabilityManager_moduleDoesNotSupportSystemCapabilities; + #pragma mark Transport + (NSError *)sdl_transport_unknownError; @@ -87,10 +92,6 @@ extern SDLErrorDomain *const SDLErrorDomainRPCStore; + (NSError *)sdl_rpcStore_invalidObjectErrorWithObject:(id)wrongObject expectedType:(Class)type; -#pragma mark System Capability Manager - -+ (NSError *)sdl_systemCapabilityManager_moduleDoesNotSupportCapabilityType; - @end @interface NSException (SDLExceptions) diff --git a/SmartDeviceLink/SDLError.m b/SmartDeviceLink/SDLError.m index 4dca1bddb..bfba582ba 100644 --- a/SmartDeviceLink/SDLError.m +++ b/SmartDeviceLink/SDLError.m @@ -291,8 +291,13 @@ + (NSError *)sdl_choiceSetManager_incorrectState:(SDLChoiceManagerState *)state #pragma mark System Capability Manager -+ (NSError *)sdl_systemCapabilityManager_moduleDoesNotSupportCapabilityType { - return [NSError errorWithDomain:SDLErrorDomainSystemCapabilityManager code:SDLSystemCapabilityManagerErrorModuleDoesNotSupportCapabilityType userInfo:nil]; ++ (NSError *)sdl_systemCapabilityManager_moduleDoesNotSupportSystemCapabilities { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Module does not understand system capabilities", nil), + NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The connected module does not support system capabilities", nil), + NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Use isCapabilitySupported to know if the feature is supported on the head unit, but no more infomration about the feature will be available on this module", nil) + }; + return [NSError errorWithDomain:SDLErrorDomainSystemCapabilityManager code:SDLSystemCapabilityManagerErrorModuleDoesNotSupportSystemCapabilities userInfo:userInfo]; } #pragma mark Transport diff --git a/SmartDeviceLink/SDLErrorConstants.h b/SmartDeviceLink/SDLErrorConstants.h index 012b45cfe..d8fd1c4f1 100644 --- a/SmartDeviceLink/SDLErrorConstants.h +++ b/SmartDeviceLink/SDLErrorConstants.h @@ -153,7 +153,7 @@ typedef NS_ENUM(NSInteger, SDLChoiceSetManagerError) { }; typedef NS_ENUM(NSInteger, SDLSystemCapabilityManagerError) { - SDLSystemCapabilityManagerErrorModuleDoesNotSupportCapabilityType = -1 + SDLSystemCapabilityManagerErrorModuleDoesNotSupportSystemCapabilities = -1 }; /** From 9cf81e10d1f127a20d04e41228c289b66a87896b Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 10 Feb 2020 16:20:37 -0500 Subject: [PATCH 20/39] Fix misnamed error --- SmartDeviceLink/SDLSystemCapabilityManager.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 69643f63f..97b80ede2 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -346,7 +346,7 @@ - (void)sdl_sendGetSystemCapabilityWithType:(SDLSystemCapabilityType)type subscr if (![response isKindOfClass:[SDLGetSystemCapabilityResponse class]]) { SDLLogE(@"GetSystemCapability failed, type: %@, did not return a GetSystemCapability response", type); if (handler == nil) { return; } - handler(nil, NO, [NSError sdl_systemCapabilityManager_moduleDoesNotSupportCapabilityType]); + handler(nil, NO, [NSError sdl_systemCapabilityManager_moduleDoesNotSupportSystemCapabilities]); return; } From 91e5d7cce5a154b391e7337895377c6f9a285edf Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 11 Feb 2020 16:52:52 -0500 Subject: [PATCH 21/39] Apply suggestions from code review Co-Authored-By: NicoleYarroch --- SmartDeviceLink/SDLError.m | 2 +- SmartDeviceLink/SDLSystemCapabilityManager.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SmartDeviceLink/SDLError.m b/SmartDeviceLink/SDLError.m index bfba582ba..6645c6b65 100644 --- a/SmartDeviceLink/SDLError.m +++ b/SmartDeviceLink/SDLError.m @@ -295,7 +295,7 @@ + (NSError *)sdl_systemCapabilityManager_moduleDoesNotSupportSystemCapabilities NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: NSLocalizedString(@"Module does not understand system capabilities", nil), NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The connected module does not support system capabilities", nil), - NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Use isCapabilitySupported to know if the feature is supported on the head unit, but no more infomration about the feature will be available on this module", nil) + NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Use isCapabilitySupported to find out if the feature is supported on the head unit, but no more information about the feature is available on this module", nil) }; return [NSError errorWithDomain:SDLErrorDomainSystemCapabilityManager code:SDLSystemCapabilityManagerErrorModuleDoesNotSupportSystemCapabilities userInfo:userInfo]; } diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.h b/SmartDeviceLink/SDLSystemCapabilityManager.h index 87ad32864..0e3419ee9 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.h +++ b/SmartDeviceLink/SDLSystemCapabilityManager.h @@ -251,11 +251,11 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla * Returns the window capability object of the primary display with the specified window ID. This is a convenient method to easily access capabilities of windows for instance widget windows of the main display. * * @param windowID The ID of the window to get capabilities - * @returns The window capability object representing the window capabilities of the window with the specified window ID or nil if the window is not known or no window capabilities exist. + * @returns The window capabilities of the window with the specified windowID or nil if the window is not known or no window capabilities exist for the window. */ - (nullable SDLWindowCapability *)windowCapabilityWithWindowID:(NSUInteger)windowID; -/// Returns whether or not the capability type is supported on the system. You can use this to check if subscribing to the capability will work. If this returns NO, then the feature is not supported by the head unit. If YES, the feature is supported by the head unit. You can subscribe to the capability type to get more information about the capability's support and features on the connected module. +/// Returns whether or not the capability type is supported on the module. You can use this to check if subscribing to the capability will work. If this returns NO, then the feature is not supported by the head unit. If YES, the feature is supported by the head unit. You can subscribe to the capability type to get more information about the capability's support and features on the connected module. /// @param type The SystemCapabilityType that will be checked. /// @return Whether or not `type` is supported by the connected head unit. - (BOOL)isCapabilitySupported:(SDLSystemCapabilityType)type NS_SWIFT_NAME(isCapabilitySupported(type:)); From 7e4614e2d28966dcf980ae647d83809b38696dc8 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 11 Feb 2020 16:55:55 -0500 Subject: [PATCH 22/39] Fixes * Numerous documentation updates * Fix observers returning the wrong value of `subscribed` * Fix focused test --- SmartDeviceLink/SDLSystemCapabilityManager.h | 22 +++++++++---------- SmartDeviceLink/SDLSystemCapabilityManager.m | 8 ++++--- .../SDLSystemCapabilityManagerSpec.m | 2 +- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.h b/SmartDeviceLink/SDLSystemCapabilityManager.h index 87ad32864..d06b94a5f 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.h +++ b/SmartDeviceLink/SDLSystemCapabilityManager.h @@ -217,7 +217,7 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla @property (nullable, strong, nonatomic, readonly) SDLWindowCapability *defaultMainWindowCapability; /** - YES if subscriptions are available on the connected module and you will automatically be notified if the value changes on the module. If NO, calls to `subscribe` methods will subscribe to updates, but the module will not automatically notify you. You will need to call `updateWithCapabilityType:completionHandler:` to force an update if you need one (though this should be rare). + YES if subscriptions are available on the connected module and you will automatically be notified if the value changes on the module. If NO, calls to `subscribe` methods will subscribe to updates, but the module will not automatically notify you. You will need to call `updateWithCapabilityType:completionHandler:` to force an update if you need one (though this should be rare). This does not apply to the `DISPLAYS` capability type which you can always subscribe to. */ @property (assign, nonatomic, readonly) BOOL supportsSubscriptions; @@ -248,10 +248,12 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla - (void)stop; /** - * Returns the window capability object of the primary display with the specified window ID. This is a convenient method to easily access capabilities of windows for instance widget windows of the main display. + * Returns the window capability of one of your app's windows with the specified window ID that is on the primary display (i.e. the head unit itself). This is a convenience method to easily access capabilities of windows such as your apps' widget windows. * - * @param windowID The ID of the window to get capabilities - * @returns The window capability object representing the window capabilities of the window with the specified window ID or nil if the window is not known or no window capabilities exist. + * To get the capabilities of the main window on the main display (i.e. your app's primary app screen on the head unit itself). + * + * @param windowID The ID of the window from which to get capabilities + * @returns The window window capabilities of the window with the specified windowID, or nil if the window is not known or no window capabilities exist. */ - (nullable SDLWindowCapability *)windowCapabilityWithWindowID:(NSUInteger)windowID; @@ -261,22 +263,20 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla - (BOOL)isCapabilitySupported:(SDLSystemCapabilityType)type NS_SWIFT_NAME(isCapabilitySupported(type:)); /** - * This method has been superceded by `subscribeToCapabilityType:` methods. You should use one of those instead, unless you only want a value once, and it must be updated. If you subscribe to a capability and are connected to a head unit that does not support subscriptions, when this method returns, it will also call all subscriptions. Therefore, you can use this method to force an update to all subscriptions of that particular type. - * - * Retrieves and updates a capability type from the remote system. This function must be called in order to retrieve the values for `navigationCapability`, `phoneCapability`, `videoStreamingCapability`, `remoteControlCapability`, and `appServicesCapabilities`. If you do not call this method first, those values will be nil. After calling this method, assuming there is no error in the handler, you may retrieve the capability you requested from the manager within the handler. + * This method has been superseded by the `subscribeToCapabilityType:` methods. You should use one of those methods instead unless you only want a value once (you don't want to keep a long-lasting observer) and it must be current (most capabilities do not need to be updated). If you have a separate subscription observer and are connected to a head unit that does not support subscriptions, when this method returns, it will also call all subscription callbacks that you've set up with the new value if there is one. Therefore, you can use this method to force an update to all subscriptions of that particular type on head units that don't support subscriptions (`supportsSubscriptions == NO`). * - * @param type The type of capability to retrieve - * @param handler The handler to be called when the retrieval is complete + * @param type The type of capability to retrieve + * @param handler The handler to be called when the retrieval is complete */ - (void)updateCapabilityType:(SDLSystemCapabilityType)type completionHandler:(SDLUpdateCapabilityHandler)handler; /// Subscribe to a particular capability type using a block callback. /// /// On v5.1.0+ systems (where `supportsSubscriptions == YES`): -/// This method will be called immediately with the current value and will be called every time the value is updated. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and a subscription will be attempted. The current cached value (`nil`) will nevertheless be returned immediately. +/// This method will be called immediately with the current value and will be called every time the value is updated. If this is the first subscription of this `SDLSystemCapabilityType`, then the current cached value of `nil` will be returned immediately, an updated value will be retrieved, and a subscription will be attempted. /// /// On sub-v5.1.0 systems (where `supportsSubscriptions == NO`): -/// The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and returned. The current cached value (`nil`) will nevertheless be returned immediately. +/// The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the current cached value of `nil` will be returned immediately, an updated value will be retrieved, and a subscription will be attempted. /// /// @param type The type of capability to subscribe to /// @param block The block to be called when the capability is updated diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 97b80ede2..f611c4053 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -623,13 +623,16 @@ - (void)sdl_callObserversForUpdate:(nullable SDLSystemCapability *)capability er } - (void)sdl_invokeObserver:(SDLSystemCapabilityObserver *)observer withCapability:(nullable SDLSystemCapability *)capability error:(nullable NSError *)error { + SDLSystemCapabilityType type = capability.systemCapabilityType; + BOOL subscribed = self.subscriptionStatus[type].boolValue || [type isEqualToEnum:SDLSystemCapabilityTypeDisplays]; + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" if (observer.block != nil) { observer.block(capability); #pragma clang diagnostic pop } else if (observer.updateBlock != nil) { - observer.updateBlock(capability, self.subscriptionStatus[capability.systemCapabilityType].boolValue, error); + observer.updateBlock(capability, subscribed, error); } else { if (![observer.observer respondsToSelector:observer.selector]) { @throw [NSException sdl_invalidSelectorExceptionWithSelector:observer.selector]; @@ -647,8 +650,7 @@ - (void)sdl_invokeObserver:(SDLSystemCapabilityObserver *)observer withCapabilit [invocation setArgument:&error atIndex:3]; } if (numberOfParametersInSelector >= 3) { - BOOL argVal = self.subscriptionStatus[capability.systemCapabilityType].boolValue; - [invocation setArgument:&argVal atIndex:4]; + [invocation setArgument:&subscribed atIndex:4]; } if (numberOfParametersInSelector >= 4) { @throw [NSException sdl_invalidSelectorExceptionWithSelector:observer.selector]; diff --git a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m index 48a4824cf..7b3447c31 100644 --- a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m +++ b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m @@ -254,7 +254,7 @@ @interface SDLSystemCapabilityManager () testSystemCapabilityManager.displayCapabilities.graphicSupported = @YES; }); - fit(@"should return true", ^{ + it(@"should return true", ^{ expect([testSystemCapabilityManager isCapabilitySupported:SDLSystemCapabilityTypeVideoStreaming]).to(beTrue()); }); }); From c0593d45a7c45c0d283f8be8739e7be333c53903 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 12 Feb 2020 13:45:07 -0500 Subject: [PATCH 23/39] Numerous fixes * Documentation fixes * Added new errors for attempts to update or subscribe when in HMI_NONE, or when attempting to update type DISPLAYS * Fixed checking `error` on `GetSystemCapabilityResponse` instead of checking `response.success` * Made return values for subscribe methods simpler * Started observing the hmi level --- SmartDeviceLink/SDLError.h | 2 + SmartDeviceLink/SDLError.m | 18 +++++++ SmartDeviceLink/SDLErrorConstants.h | 4 +- SmartDeviceLink/SDLSystemCapabilityManager.h | 6 +-- SmartDeviceLink/SDLSystemCapabilityManager.m | 53 ++++++++++++++------ 5 files changed, 65 insertions(+), 18 deletions(-) diff --git a/SmartDeviceLink/SDLError.h b/SmartDeviceLink/SDLError.h index 06d647480..0351239d5 100644 --- a/SmartDeviceLink/SDLError.h +++ b/SmartDeviceLink/SDLError.h @@ -80,6 +80,8 @@ extern SDLErrorDomain *const SDLErrorDomainRPCStore; #pragma mark System Capability Manager + (NSError *)sdl_systemCapabilityManager_moduleDoesNotSupportSystemCapabilities; ++ (NSError *)sdl_systemCapabilityManager_cannotUpdateInHMINONE; ++ (NSError *)sdl_systemCapabilityManager_cannotUpdateTypeDISPLAYS; #pragma mark Transport diff --git a/SmartDeviceLink/SDLError.m b/SmartDeviceLink/SDLError.m index 6645c6b65..916324588 100644 --- a/SmartDeviceLink/SDLError.m +++ b/SmartDeviceLink/SDLError.m @@ -300,6 +300,24 @@ + (NSError *)sdl_systemCapabilityManager_moduleDoesNotSupportSystemCapabilities return [NSError errorWithDomain:SDLErrorDomainSystemCapabilityManager code:SDLSystemCapabilityManagerErrorModuleDoesNotSupportSystemCapabilities userInfo:userInfo]; } ++ (NSError *)sdl_systemCapabilityManager_cannotUpdateInHMINONE { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"System capabilities cannot be updated in HMI NONE.", nil), + NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The system capability manager attempted to subscribe or update a system capability in HMI NONE, which is not allowed.", nil), + NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Wait until you are in HMI BACKGROUND, LIMITED, OR FULL before subscribing or updating a capability.", nil) + }; + return [NSError errorWithDomain:SDLErrorDomainSystemCapabilityManager code:SDLSystemCapabilityManagerErrorHMINone userInfo:userInfo]; +} + ++ (NSError *)sdl_systemCapabilityManager_cannotUpdateTypeDISPLAYS { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"System capability type DISPLAYS cannot be updated.", nil), + NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The system capability manager attempted to update system capability type DISPLAYS, which is not allowed.", nil), + NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Subscribe to DISPLAYS to automatically receive updates or retrieve a cached display capability value directly from the SystemCapabilityManager.", nil) + }; + return [NSError errorWithDomain:SDLErrorDomainSystemCapabilityManager code:SDLSystemCapabilityManagerErrorCannotUpdateTypeDisplays userInfo:userInfo]; +} + #pragma mark Transport + (NSError *)sdl_transport_unknownError { diff --git a/SmartDeviceLink/SDLErrorConstants.h b/SmartDeviceLink/SDLErrorConstants.h index d8fd1c4f1..a4e80c34f 100644 --- a/SmartDeviceLink/SDLErrorConstants.h +++ b/SmartDeviceLink/SDLErrorConstants.h @@ -153,7 +153,9 @@ typedef NS_ENUM(NSInteger, SDLChoiceSetManagerError) { }; typedef NS_ENUM(NSInteger, SDLSystemCapabilityManagerError) { - SDLSystemCapabilityManagerErrorModuleDoesNotSupportSystemCapabilities = -1 + SDLSystemCapabilityManagerErrorModuleDoesNotSupportSystemCapabilities = -1, + SDLSystemCapabilityManagerErrorHMINone = -2, + SDLSystemCapabilityManagerErrorCannotUpdateTypeDisplays = -3 }; /** diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.h b/SmartDeviceLink/SDLSystemCapabilityManager.h index bb452f0b3..423527bab 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.h +++ b/SmartDeviceLink/SDLSystemCapabilityManager.h @@ -280,7 +280,7 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla /// /// @param type The type of capability to subscribe to /// @param block The block to be called when the capability is updated -/// @return An object that can be used to unsubscribe the block using unsubscribeFromCapabilityType:withObserver: by passing it in the observer callback, or nil if subscriptions aren't available on this head unit +/// @return An object that can be used to unsubscribe the block using unsubscribeFromCapabilityType:withObserver: by passing it in the observer callback, or nil if the manager can't attempt the subscription for some reason (such as the app being in HMI_NONE and the type is not DISPLAYS). - (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withBlock:(SDLCapabilityUpdateHandler)block __deprecated_msg("use subscribeToCapabilityType:withUpdateHandler: instead"); /// Subscribe to a particular capability type using a handler callback. @@ -293,7 +293,7 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla /// @param type The type of capability to subscribe to /// @param handler The block to be called when the capability is updated with an error if one occurs -/// @return An object that can be used to unsubscribe the block using unsubscribeFromCapabilityType:withObserver: by passing it in the observer callback, or nil if subscriptions aren't available on this head unit +/// @return An object that can be used to unsubscribe the block using unsubscribeFromCapabilityType:withObserver: by passing it in the observer callback, or nil if the manager can't attempt the subscription for some reason (such as the app being in HMI_NONE and the type is not DISPLAYS). - (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withUpdateHandler:(SDLCapabilityUpdateWithErrorHandler)handler NS_SWIFT_NAME(subscribe(capabilityType:updateHandler:)); /** @@ -318,7 +318,7 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla * @param type The type of the system capability to subscribe to * @param observer The object that will have `selector` called whenever the capability is updated * @param selector The selector on `observer` that will be called whenever the capability is updated - * @return Whether or not the subscription succeeded. `NO` if the connected system doesn't support capability subscriptions, or if the `selector` doesn't support the correct parameters (see above). + * @return YES if the manager is attempting the subscription, or NO if the manager can't attempt the subscription for some reason (such as the app being in HMI_NONE and the type is not DISPLAYS), or the selector doesn't contain the correct number of parameters. */ - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id)observer selector:(SEL)selector; diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index f611c4053..5eb06def4 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -75,6 +75,7 @@ @interface SDLSystemCapabilityManager () @property (nullable, strong, nonatomic) SDLSystemCapability *lastReceivedCapability; @property (assign, nonatomic) BOOL shouldConvertDeprecatedDisplayCapabilities; +@property (strong, nonatomic) SDLHMILevel currentHMILevel; @end @@ -318,7 +319,16 @@ - (nullable SDLSystemCapability *)sdl_cachedCapabilityForType:(SDLSystemCapabili - (void)updateCapabilityType:(SDLSystemCapabilityType)type completionHandler:(SDLUpdateCapabilityHandler)handler { SDLLogV(@"Updating capability type: %@", type); - if (self.supportsSubscriptions) { + if ([self.currentHMILevel isEqualToEnum:SDLHMILevelNone] && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { + SDLLogE(@"Attempted to update type: %@ in HMI level NONE, which is not allowed. Please wait until you are in HMI BACKGROUND, LIMITED, or FULL before attempting to update any SystemCapabilityType DISPLAYS.", type); + return handler([NSError sdl_systemCapabilityManager_cannotUpdateInHMINONE], self); + } else if ([type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { + SDLLogE(@"Attempted to update type DISPLAYS, which is not allowed. You are always subscribed to displays, please either pull the cached data directly or subscribe for updates to DISPLAYS."); + return handler([NSError sdl_systemCapabilityManager_cannotUpdateTypeDISPLAYS], self); + } + + // If we support subscriptions and we're already subscribed + if (self.supportsSubscriptions && [self.subscriptionStatus[type] isEqualToNumber:@YES]) { // Just return the cached data because we get `onSystemCapability` callbacks handler(nil, self); } else { @@ -350,9 +360,8 @@ - (void)sdl_sendGetSystemCapabilityWithType:(SDLSystemCapabilityType)type subscr return; } - if (error != nil) { + if (response.success.boolValue == false) { SDLLogE(@"GetSystemCapability failed, type: %@, error: %@", type, error); - // An error is returned if the request was unsuccessful or if a Generic Response was returned if (handler == nil) { return; } handler(nil, NO, error); return; @@ -501,9 +510,14 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n #pragma mark - Manager Subscriptions - (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withBlock:(SDLCapabilityUpdateHandler)block { - SDLLogD(@"Subscribing to capability type: %@ with a handler", type); - SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:[[NSObject alloc] init] block:block]; + SDLLogD(@"Subscribing to capability type: %@ with a handler (DEPRECATED)", type); + 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); + return nil; + } + + SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:[[NSObject alloc] init] block:block]; 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 array]; @@ -528,8 +542,13 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n - (nullable id)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); + return nil; + } + + SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:[[NSObject alloc] init] updateHandler:handler]; 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 array]; @@ -560,6 +579,11 @@ - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id return NO; } + 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); + return NO; + } + SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:observer selector:selector]; if (self.capabilityObservers[type] == nil) { SDLLogD(@"This is the first subscription to capability type: %@, sending a GetSystemCapability with subscribe true", type); @@ -575,16 +599,12 @@ - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id }]; } } - [self.capabilityObservers[type] addObject:observerObject]; - // Call immediately with the cached value + // Store the observer and call it immediately with the cached value + [self.capabilityObservers[type] addObject:observerObject]; [self sdl_invokeObserver:observerObject withCapability:[self sdl_cachedCapabilityForType:type] error:nil]; - // If it doesn't support subscriptions, get the data only once (subscribe can be YES, it will be ignored), then return NO. DISPLAYS always works due to old-style SetDisplayLayoutResponse updates, but otherwise, subscriptions won't work. - if (!self.supportsSubscriptions && ![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { return NO; - } else { - return YES; - } + return YES; } - (void)unsubscribeFromCapabilityType:(SDLSystemCapabilityType)type withObserver:(id)observer { @@ -597,7 +617,7 @@ - (void)unsubscribeFromCapabilityType:(SDLSystemCapabilityType)type withObserver SDLLogD(@"Removing the last subscription to type %@, sending a GetSystemCapability with subscribe false (will unsubscribe)", type); self.capabilityObservers[type] = nil; - // We don't want to send this for the displays type because that's automatically subscribed + // We don't want to send this for the displays type because that's automatically subscribed and must remain subscribed if (![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { [self sdl_sendGetSystemCapabilityWithType:type subscribe:@NO completionHandler:nil]; } @@ -760,6 +780,11 @@ - (void)sdl_systemCapabilityResponseNotification:(SDLRPCResponseNotification *)n [self sdl_saveSystemCapability:systemCapabilityResponse.systemCapability error:nil completionHandler:nil]; } +- (void)sdl_hmiStatusNotification:(SDLRPCNotificationNotification *)notification { + SDLOnHMIStatus *onHMIStatus = (SDLOnHMIStatus *)notification.notification; + self.currentHMILevel = onHMIStatus.hmiLevel; +} + @end NS_ASSUME_NONNULL_END From 9773a0dfeca284c7a4eecdf4c8078f43bfe5c89b Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 12 Feb 2020 13:45:30 -0500 Subject: [PATCH 24/39] Test fixes * Also minor documentation fixes --- SmartDeviceLink/SDLSystemCapabilityObserver.h | 2 + .../TestSystemCapabilityObserver.h | 3 + .../TestSystemCapabilityObserver.m | 25 +++++++ .../SDLSystemCapabilityManagerSpec.m | 75 ++++++++++++------- 4 files changed, 79 insertions(+), 26 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityObserver.h b/SmartDeviceLink/SDLSystemCapabilityObserver.h index f45f3869d..11a6acfc1 100644 --- a/SmartDeviceLink/SDLSystemCapabilityObserver.h +++ b/SmartDeviceLink/SDLSystemCapabilityObserver.h @@ -12,8 +12,10 @@ NS_ASSUME_NONNULL_BEGIN +/// A handler mirroring the one in SDLSystemCapabilityManager.h for `initWithObserver:block:` typedef void (^SDLCapabilityUpdateHandler)(SDLSystemCapability *capability); +/// A handler mirroring the one in SDLSystemCapabilityManager.h for `initWithObserver:updateHandler:` typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability *_Nullable capability, BOOL subscribed, NSError *_Nullable error); /** diff --git a/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.h b/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.h index 080662b7a..1d602f1f3 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.h +++ b/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.h @@ -15,6 +15,9 @@ NS_ASSUME_NONNULL_BEGIN @interface TestSystemCapabilityObserver : NSObject @property (assign, nonatomic) NSUInteger selectorCalledCount; +@property (strong, nonatomic, nullable) NSMutableArray *capabilitiesReceived; +@property (strong, nonatomic, nullable) NSMutableArray *errorsReceived; +@property (strong, nonatomic, nullable) NSMutableArray *subscribedValuesReceived; - (void)capabilityUpdated; - (void)capabilityUpdatedWithCapability:(SDLSystemCapability *)capability; diff --git a/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.m b/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.m index 1c1672848..02696cb1b 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.m +++ b/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.m @@ -17,6 +17,9 @@ - (instancetype)init { if (!self) { return nil; } _selectorCalledCount = 0; + _capabilitiesReceived = [NSMutableArray array]; + _errorsReceived = [NSMutableArray array]; + _subscribedValuesReceived = [NSMutableArray array]; return self; } @@ -27,14 +30,36 @@ - (void)capabilityUpdated { - (void)capabilityUpdatedWithCapability:(SDLSystemCapability *)capability { self.selectorCalledCount++; + + if (capability != nil) { + [self.capabilitiesReceived addObject:capability]; + } } - (void)capabilityUpdatedWithCapability:(SDLSystemCapability *)capability error:(NSError *)error { self.selectorCalledCount++; + + if (capability != nil) { + [self.capabilitiesReceived addObject:capability]; + } + + if (error != nil) { + [self.errorsReceived addObject:error]; + } } - (void)capabilityUpdatedWithCapability:(SDLSystemCapability *)capability error:(NSError *)error subscribed:(BOOL)subscribed { self.selectorCalledCount++; + + if (capability != nil) { + [self.capabilitiesReceived addObject:capability]; + } + + if (error != nil) { + [self.errorsReceived addObject:error]; + } + + [self.subscribedValuesReceived addObject:@(subscribed)]; } @end diff --git a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m index 7b3447c31..69e65c45e 100644 --- a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m +++ b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m @@ -66,7 +66,7 @@ @interface SDLSystemCapabilityManager () QuickSpecBegin(SDLSystemCapabilityManagerSpec) -describe(@"System capability manager", ^{ +fdescribe(@"System capability manager", ^{ __block SDLSystemCapabilityManager *testSystemCapabilityManager = nil; __block TestConnectionManager *testConnectionManager = nil; @@ -520,7 +520,7 @@ @interface SDLSystemCapabilityManager () }); }); - context(@"When sending a GetSystemCapability request", ^{ + context(@"When sending a updateCapabilityType request", ^{ __block SDLGetSystemCapabilityResponse *testGetSystemCapabilityResponse = nil; __block SDLPhoneCapability *testPhoneCapability = nil; @@ -542,17 +542,14 @@ @interface SDLSystemCapabilityManager () }); it(@"should should not save the capabilities", ^{ - waitUntilTimeout(1, ^(void (^done)(void)) { - [testSystemCapabilityManager updateCapabilityType:testGetSystemCapabilityResponse.systemCapability.systemCapabilityType completionHandler:^(NSError * _Nullable error, SDLSystemCapabilityManager * _Nonnull systemCapabilityManager) { - expect(error).to(equal(testConnectionManager.defaultError)); - expect(systemCapabilityManager.phoneCapability).to(beNil()); - done(); - }]; + [testSystemCapabilityManager updateCapabilityType:SDLSystemCapabilityTypePhoneCall completionHandler:^(NSError * _Nullable error, SDLSystemCapabilityManager * _Nonnull systemCapabilityManager) { + expect(error).toEventually(equal(testConnectionManager.defaultError)); + expect(systemCapabilityManager.phoneCapability).toEventually(beNil()); + }]; - [NSThread sleepForTimeInterval:0.1]; + [NSThread sleepForTimeInterval:0.1]; - [testConnectionManager respondToLastRequestWithResponse:testGetSystemCapabilityResponse]; - }); + [testConnectionManager respondToLastRequestWithResponse:testGetSystemCapabilityResponse]; }); }); @@ -562,17 +559,14 @@ @interface SDLSystemCapabilityManager () }); it(@"should save the capabilitity", ^{ - waitUntilTimeout(1, ^(void (^done)(void)){ - [testSystemCapabilityManager updateCapabilityType:testGetSystemCapabilityResponse.systemCapability.systemCapabilityType completionHandler:^(NSError * _Nullable error, SDLSystemCapabilityManager * _Nonnull systemCapabilityManager) { - expect(testSystemCapabilityManager.phoneCapability).to(equal(testPhoneCapability)); - expect(error).to(beNil()); - done(); - }]; + [testSystemCapabilityManager updateCapabilityType:SDLSystemCapabilityTypePhoneCall completionHandler:^(NSError * _Nullable error, SDLSystemCapabilityManager * _Nonnull systemCapabilityManager) { + expect(testSystemCapabilityManager.phoneCapability).toEventually(equal(testPhoneCapability)); + expect(error).toEventually(beNil()); + }]; - [NSThread sleepForTimeInterval:0.1]; + [NSThread sleepForTimeInterval:0.1]; - [testConnectionManager respondToLastRequestWithResponse:testGetSystemCapabilityResponse]; - }); + [testConnectionManager respondToLastRequestWithResponse:testGetSystemCapabilityResponse]; }); }); @@ -620,6 +614,8 @@ @interface SDLSystemCapabilityManager () describe(@"subscribing to capability types", ^{ __block TestSystemCapabilityObserver *phoneObserver = nil; __block TestSystemCapabilityObserver *navigationObserver = nil; + __block TestSystemCapabilityObserver *videoStreamingObserver = nil; + __block TestSystemCapabilityObserver *displaysObserver = nil; __block NSUInteger observerTriggeredCount = 0; __block NSUInteger handlerTriggeredCount = 0; @@ -632,20 +628,23 @@ @interface SDLSystemCapabilityManager () phoneObserver = [[TestSystemCapabilityObserver alloc] init]; [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withObserver:phoneObserver selector:@selector(capabilityUpdatedWithCapability:)]; navigationObserver = [[TestSystemCapabilityObserver alloc] init]; - [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypeNavigation withObserver:navigationObserver selector:@selector(capabilityUpdatedWithCapability:)]; + [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypeNavigation withObserver:navigationObserver selector:@selector(capabilityUpdatedWithCapability:error:)]; + videoStreamingObserver = [[TestSystemCapabilityObserver alloc] init]; + [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypeVideoStreaming withObserver:videoStreamingObserver selector:@selector(capabilityUpdatedWithCapability:error:subscribed:)]; + [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypeDisplays withObserver:displaysObserver selector:@selector(capabilityUpdatedWithCapability:error:subscribed:)]; }); describe(@"when observers aren't supported", ^{ __block BOOL observationSuccess = NO; beforeEach(^{ - [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"5.0.0"]; // doesn't subscriptions + [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"5.0.0"]; // no subscriptions observationSuccess = [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withObserver:phoneObserver selector:@selector(capabilityUpdatedWithCapability:)]; }); it(@"should fail to subscribe", ^{ - expect(observationSuccess).to(beFalse()); + expect(observationSuccess).to(beTrue()); }); }); @@ -673,10 +672,18 @@ @interface SDLSystemCapabilityManager () }); it(@"should notify subscribers of the new data", ^{ - expect(phoneObserver.selectorCalledCount).toEventually(equal(2)); expect(handlerTriggeredCount).toEventually(equal(2)); expect(observerTriggeredCount).toEventually(equal(2)); + + expect(phoneObserver.selectorCalledCount).toEventually(equal(2)); + expect(navigationObserver.selectorCalledCount).toEventually(equal(1)); + + expect(videoStreamingObserver.selectorCalledCount).toEventually(equal(1)); + + expect(displaysObserver.selectorCalledCount).toEventually(equal(1)); + expect(displaysObserver.subscribedValuesReceived).toEventually(haveCount(1)); + expect(displaysObserver.subscribedValuesReceived.firstObject).toEventually(beTrue()); }); describe(@"unsubscribing", ^{ @@ -693,10 +700,16 @@ @interface SDLSystemCapabilityManager () }); it(@"should not notify the subscriber of the new data", ^{ - expect(phoneObserver.selectorCalledCount).toEventually(equal(2)); // No change from above expect(handlerTriggeredCount).toEventually(equal(2)); expect(observerTriggeredCount).toEventually(equal(2)); + + expect(phoneObserver.selectorCalledCount).toEventually(equal(2)); // No change from above + expect(navigationObserver.selectorCalledCount).toEventually(equal(1)); + + expect(videoStreamingObserver.selectorCalledCount).toEventually(equal(1)); + + expect(displaysObserver.selectorCalledCount).toEventually(equal(1)); }); }); }); @@ -724,10 +737,18 @@ @interface SDLSystemCapabilityManager () }); it(@"should notify subscribers of the new data", ^{ - expect(phoneObserver.selectorCalledCount).toEventually(equal(2)); expect(handlerTriggeredCount).toEventually(equal(2)); expect(observerTriggeredCount).toEventually(equal(2)); + + expect(phoneObserver.selectorCalledCount).toEventually(equal(2)); + expect(navigationObserver.selectorCalledCount).toEventually(equal(1)); + + expect(videoStreamingObserver.selectorCalledCount).toEventually(equal(1)); + + expect(displaysObserver.selectorCalledCount).toEventually(equal(1)); + expect(displaysObserver.subscribedValuesReceived).toEventually(haveCount(1)); + expect(displaysObserver.subscribedValuesReceived.firstObject).toEventually(beTrue()); }); describe(@"unsubscribing", ^{ @@ -746,6 +767,8 @@ @interface SDLSystemCapabilityManager () expect(phoneObserver.selectorCalledCount).toEventually(equal(2)); // No change from above expect(observerTriggeredCount).toEventually(equal(2)); expect(navigationObserver.selectorCalledCount).toEventually(equal(1)); + expect(videoStreamingObserver.selectorCalledCount).toEventually(equal(1)); + expect(displaysObserver.selectorCalledCount).toEventually(equal(1)); }); }); }); From 22b418654f4eb81c865022d20e99628c37c060cb Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 12 Feb 2020 14:22:14 -0500 Subject: [PATCH 25/39] Update error language --- SmartDeviceLink/SDLError.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SmartDeviceLink/SDLError.m b/SmartDeviceLink/SDLError.m index 916324588..d90eaa6c0 100644 --- a/SmartDeviceLink/SDLError.m +++ b/SmartDeviceLink/SDLError.m @@ -411,7 +411,7 @@ + (NSException *)sdl_invalidLockscreenSetupException { + (NSException *)sdl_invalidSelectorExceptionWithSelector:(SEL)selector { return [NSException exceptionWithName:@"com.sdl.systemCapabilityManager.selectorException" - reason:[NSString stringWithFormat:@"Capability observation selector: %@ does not match possible selectors, which must have between 0 and 3 parameters", NSStringFromSelector(selector)] + reason:[NSString stringWithFormat:@"Capability observation selector: %@ does not match possible selectors, which must have between 0 and 3 parameters, or is not a selector on the observer object. Check that your selector is formatted correctly, and that your observer is not nil. You should unsubscribe an observer before it goes to nil.", NSStringFromSelector(selector)] userInfo:nil]; } From c9eefb2c55e4ba4bf734050b67f55ae119d9458f Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 12 Feb 2020 14:27:06 -0500 Subject: [PATCH 26/39] Fixes to SystemCapabilityManager * When sending a GetSystemCapability when subscribing or unsubscribing, we don't need to manually invoke the observer. This is automatically done in the `sdl_sendGetSystemCapabilityWithType:` method by saving the capability that was retrieved. * Before calling observers, run a loop to check if any observers have gone to `nil` and remove them, then unsubscribe if we have no more observers for that SC type. --- SmartDeviceLink/SDLSystemCapabilityManager.m | 75 ++++++++++++-------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 5eb06def4..296b10479 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -524,12 +524,7 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n // We don't want to send this for the displays type because that's automatically subscribed if (![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { - __weak typeof(self) weakself = self; - [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:^(SDLSystemCapability * _Nullable capability, BOOL subscribed, NSError * _Nullable error) { - if (error != nil) { - [weakself sdl_invokeObserver:observerObject withCapability:capability error:error]; - } - }]; + [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:nil]; } } [self.capabilityObservers[type] addObject:observerObject]; @@ -555,12 +550,7 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n // We don't want to send this for the displays type because that's automatically subscribed if (![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { - __weak typeof(self) weakself = self; - [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:^(SDLSystemCapability * _Nullable capability, BOOL subscribed, NSError * _Nullable error) { - if (error != nil) { - [weakself sdl_invokeObserver:observerObject withCapability:capability error:error]; - } - }]; + [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:nil]; } } [self.capabilityObservers[type] addObject:observerObject]; @@ -584,6 +574,11 @@ - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id return NO; } + if (observer == nil) { + SDLLogE(@"Attempted to subscribe to type: %@ with a selector on a *nil* observer, which will always fail.", type); + return NO; + } + SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:observer selector:selector]; if (self.capabilityObservers[type] == nil) { SDLLogD(@"This is the first subscription to capability type: %@, sending a GetSystemCapability with subscribe true", type); @@ -591,12 +586,7 @@ - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id // We don't want to send this for the displays type because that's automatically subscribed if (![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { - __weak typeof(self) weakself = self; - [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:^(SDLSystemCapability * _Nullable capability, BOOL subscribed, NSError * _Nullable error) { - if (error != nil) { - [weakself sdl_invokeObserver:observerObject withCapability:capability error:error]; - } - }]; + [self sdl_sendGetSystemCapabilityWithType:type subscribe:@YES completionHandler:nil]; } } @@ -613,16 +603,7 @@ - (void)unsubscribeFromCapabilityType:(SDLSystemCapabilityType)type withObserver if ([observer isEqual:capabilityObserver.observer] && self.capabilityObservers[type] != nil) { [self.capabilityObservers[type] removeObject:capabilityObserver]; - if (self.capabilityObservers[type].count == 0 && self.supportsSubscriptions) { - SDLLogD(@"Removing the last subscription to type %@, sending a GetSystemCapability with subscribe false (will unsubscribe)", type); - self.capabilityObservers[type] = nil; - - // We don't want to send this for the displays type because that's automatically subscribed and must remain subscribed - if (![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { - [self sdl_sendGetSystemCapabilityWithType:type subscribe:@NO completionHandler:nil]; - } - } - + [self sdl_removeNilObserversAndUnsubscribeIfNecessary]; break; } } @@ -634,12 +615,15 @@ - (void)unsubscribeFromCapabilityType:(SDLSystemCapabilityType)type withObserver - (void)sdl_callObserversForUpdate:(nullable SDLSystemCapability *)capability error:(nullable NSError *)error handler:(nullable SDLCapabilityUpdateWithErrorHandler)handler { SDLSystemCapabilityType type = capability.systemCapabilityType; SDLLogV(@"Calling observers for type: %@ with update: %@", type, capability); + + [self sdl_removeNilObserversAndUnsubscribeIfNecessary]; + for (SDLSystemCapabilityObserver *observer in self.capabilityObservers[type]) { [self sdl_invokeObserver:observer withCapability:capability error:error]; } if (handler == nil) { return; } - handler(capability, self.subscriptionStatus[type].boolValue, nil); + handler(capability, self.subscriptionStatus[type].boolValue, error); } - (void)sdl_invokeObserver:(SDLSystemCapabilityObserver *)observer withCapability:(nullable SDLSystemCapability *)capability error:(nullable NSError *)error { @@ -680,6 +664,39 @@ - (void)sdl_invokeObserver:(SDLSystemCapabilityObserver *)observer withCapabilit } } +- (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 From 3d71e7fbb4370d80d4804acfb4a1bd1f847dce5b Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 12 Feb 2020 14:52:06 -0500 Subject: [PATCH 27/39] Fixes for SystemCapabilityManager * Send the type separately to `invokeObservers` because if the capability is nil, we can't get the type, which messes up the subscribed BOOL if the type is DISPLAYS --- SmartDeviceLink/SDLSystemCapabilityManager.m | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 296b10479..960b1b8e5 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -530,7 +530,7 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n [self.capabilityObservers[type] addObject:observerObject]; // Call the block immediately with the cached value - [self sdl_invokeObserver:observerObject withCapability:[self sdl_cachedCapabilityForType:type] error:nil]; + [self sdl_invokeObserver:observerObject withCapabilityType:type capability:[self sdl_cachedCapabilityForType:type] error:nil]; return observerObject.observer; } @@ -556,7 +556,7 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n [self.capabilityObservers[type] addObject:observerObject]; // Call the block immediately with the cached value - [self sdl_invokeObserver:observerObject withCapability:[self sdl_cachedCapabilityForType:type] error:nil]; + [self sdl_invokeObserver:observerObject withCapabilityType:type capability:[self sdl_cachedCapabilityForType:type] error:nil]; return observerObject.observer; } @@ -592,7 +592,7 @@ - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id // Store the observer and call it immediately with the cached value [self.capabilityObservers[type] addObject:observerObject]; - [self sdl_invokeObserver:observerObject withCapability:[self sdl_cachedCapabilityForType:type] error:nil]; + [self sdl_invokeObserver:observerObject withCapabilityType:type capability:[self sdl_cachedCapabilityForType:type] error:nil]; return YES; } @@ -619,15 +619,14 @@ - (void)sdl_callObserversForUpdate:(nullable SDLSystemCapability *)capability er [self sdl_removeNilObserversAndUnsubscribeIfNecessary]; for (SDLSystemCapabilityObserver *observer in self.capabilityObservers[type]) { - [self sdl_invokeObserver:observer withCapability:capability error:error]; + [self sdl_invokeObserver:observer withCapabilityType:type capability:capability error:error]; } if (handler == nil) { return; } handler(capability, self.subscriptionStatus[type].boolValue, error); } -- (void)sdl_invokeObserver:(SDLSystemCapabilityObserver *)observer withCapability:(nullable SDLSystemCapability *)capability error:(nullable NSError *)error { - SDLSystemCapabilityType type = capability.systemCapabilityType; +- (void)sdl_invokeObserver:(SDLSystemCapabilityObserver *)observer withCapabilityType:(SDLSystemCapabilityType)type capability:(nullable SDLSystemCapability *)capability error:(nullable NSError *)error { BOOL subscribed = self.subscriptionStatus[type].boolValue || [type isEqualToEnum:SDLSystemCapabilityTypeDisplays]; #pragma clang diagnostic push From cb3bd6109cb7d9018a644d564713f47e796eab74 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 12 Feb 2020 14:53:01 -0500 Subject: [PATCH 28/39] Fix SystemCapabilityManager tests * Fix the displaysObserver not being properly generated --- SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m index 69e65c45e..ceef56e67 100644 --- a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m +++ b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m @@ -631,6 +631,7 @@ @interface SDLSystemCapabilityManager () [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypeNavigation withObserver:navigationObserver selector:@selector(capabilityUpdatedWithCapability:error:)]; videoStreamingObserver = [[TestSystemCapabilityObserver alloc] init]; [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypeVideoStreaming withObserver:videoStreamingObserver selector:@selector(capabilityUpdatedWithCapability:error:subscribed:)]; + displaysObserver = [[TestSystemCapabilityObserver alloc] init]; [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypeDisplays withObserver:displaysObserver selector:@selector(capabilityUpdatedWithCapability:error:subscribed:)]; }); @@ -739,7 +740,7 @@ @interface SDLSystemCapabilityManager () it(@"should notify subscribers of the new data", ^{ expect(handlerTriggeredCount).toEventually(equal(2)); expect(observerTriggeredCount).toEventually(equal(2)); - + expect(phoneObserver.selectorCalledCount).toEventually(equal(2)); expect(navigationObserver.selectorCalledCount).toEventually(equal(1)); From da1af9cd96b86659512d73065bda3cfe75f6f4f8 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 12 Feb 2020 14:58:42 -0500 Subject: [PATCH 29/39] Add additional SystemCapabilityManager tests * Add tests around non-DISPLAYS subscribed bool for subscriptions with 3 selector parameters --- SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m index ceef56e67..57020b0cc 100644 --- a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m +++ b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m @@ -681,6 +681,8 @@ @interface SDLSystemCapabilityManager () expect(navigationObserver.selectorCalledCount).toEventually(equal(1)); expect(videoStreamingObserver.selectorCalledCount).toEventually(equal(1)); + expect(videoStreamingObserver.subscribedValuesReceived).toEventually(haveCount(1)); + expect(videoStreamingObserver.subscribedValuesReceived.firstObject).toEventually(beFalse()); expect(displaysObserver.selectorCalledCount).toEventually(equal(1)); expect(displaysObserver.subscribedValuesReceived).toEventually(haveCount(1)); @@ -746,6 +748,8 @@ @interface SDLSystemCapabilityManager () expect(navigationObserver.selectorCalledCount).toEventually(equal(1)); expect(videoStreamingObserver.selectorCalledCount).toEventually(equal(1)); + expect(videoStreamingObserver.subscribedValuesReceived).toEventually(haveCount(1)); + expect(videoStreamingObserver.subscribedValuesReceived.firstObject).toEventually(beFalse()); expect(displaysObserver.selectorCalledCount).toEventually(equal(1)); expect(displaysObserver.subscribedValuesReceived).toEventually(haveCount(1)); From 12e08d38f097981e1a1b07ac2f8993965c55ccd7 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 18 Feb 2020 11:44:59 -0500 Subject: [PATCH 30/39] Apply suggestions from code review Co-Authored-By: NicoleYarroch --- SmartDeviceLink/SDLError.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SmartDeviceLink/SDLError.m b/SmartDeviceLink/SDLError.m index d90eaa6c0..b06d3dc3e 100644 --- a/SmartDeviceLink/SDLError.m +++ b/SmartDeviceLink/SDLError.m @@ -312,7 +312,7 @@ + (NSError *)sdl_systemCapabilityManager_cannotUpdateInHMINONE { + (NSError *)sdl_systemCapabilityManager_cannotUpdateTypeDISPLAYS { NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: NSLocalizedString(@"System capability type DISPLAYS cannot be updated.", nil), - NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The system capability manager attempted to update system capability type DISPLAYS, which is not allowed.", nil), + NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The system capability manager attempted to update system capability type DISPLAYS, which is not allowed.", nil), NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Subscribe to DISPLAYS to automatically receive updates or retrieve a cached display capability value directly from the SystemCapabilityManager.", nil) }; return [NSError errorWithDomain:SDLErrorDomainSystemCapabilityManager code:SDLSystemCapabilityManagerErrorCannotUpdateTypeDisplays userInfo:userInfo]; From 61bf46e4340f0f2b62f9ea7b5defdb2a846359d8 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 18 Feb 2020 11:55:57 -0500 Subject: [PATCH 31/39] Fixed based on review --- SmartDeviceLink/SDLSystemCapabilityManager.h | 4 +++- SmartDeviceLink/SDLSystemCapabilityManager.m | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.h b/SmartDeviceLink/SDLSystemCapabilityManager.h index 423527bab..36c55799a 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.h +++ b/SmartDeviceLink/SDLSystemCapabilityManager.h @@ -286,7 +286,9 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla /// Subscribe to a particular capability type using a handler callback. /// /// On v5.1.0+ systems (where `supportsSubscriptions == YES`): -/// This method will be called immediately with the current value and will be called every time the value is updated. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and a subscription will be attempted. The current cached value (`nil`) will nevertheless be returned immediately. +/// This method will be called immediately with the current value and will be called every time the value is updated. If this is the first subscription of this `SDLSystemCapabilityType`, then the current cached value of `nil` will be returned immediately, an updated value will be retrieved, and a subscription will be attempted. +/// +/// Note that when the cached value is returned, the `subscribed` flag on the handler will be false until the subscription completes successfully and a new value is retrieved. /// /// On sub-v5.1.0 systems (where `supportsSubscriptions == NO`): /// The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and returned. The current cached value (`nil`) will nevertheless be returned immediately. diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 960b1b8e5..9d7301c3c 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -96,6 +96,8 @@ - (instancetype)initWithConnectionManager:(id)manager _capabilityObservers = [NSMutableDictionary dictionary]; _subscriptionStatus = [NSMutableDictionary dictionary]; + _currentHMILevel = SDLHMILevelNone; + [self sdl_registerForNotifications]; return self; @@ -131,6 +133,8 @@ - (void)stop { [_capabilityObservers removeAllObjects]; [_subscriptionStatus removeAllObjects]; + _currentHMILevel = SDLHMILevelNone; + _shouldConvertDeprecatedDisplayCapabilities = YES; } From 464fb83aa6a31c87ca2d4a5a583c93539fe21210 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 18 Feb 2020 12:08:25 -0500 Subject: [PATCH 32/39] Fix tests --- .../SDLSystemCapabilityManagerSpec.m | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m index 57020b0cc..41cf72929 100644 --- a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m +++ b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m @@ -31,6 +31,7 @@ #import "SDLSetDisplayLayoutResponse.h" #import "SDLSoftButtonCapabilities.h" #import "SDLSystemCapability.h" +#import "SDLSystemCapabilityObserver.h" #import "SDLSystemCapabilityManager.h" #import "SDLTextField.h" #import "SDLVersion.h" @@ -40,9 +41,12 @@ #import "TestConnectionManager.h" #import "TestSystemCapabilityObserver.h" +typedef NSString * SDLServiceID; @interface SDLSystemCapabilityManager () +@property (weak, nonatomic) id connectionManager; + @property (nullable, strong, nonatomic, readwrite) NSArray *displays; @property (nullable, strong, nonatomic, readwrite) SDLDisplayCapabilities *displayCapabilities; @property (nullable, strong, nonatomic, readwrite) SDLHMICapabilities *hmiCapabilities; @@ -61,12 +65,23 @@ @interface SDLSystemCapabilityManager () @property (nullable, strong, nonatomic, readwrite) SDLRemoteControlCapabilities *remoteControlCapability; @property (nullable, strong, nonatomic, readwrite) SDLSeatLocationCapability *seatLocationCapability; +@property (nullable, strong, nonatomic) NSMutableDictionary *appServicesCapabilitiesDictionary; + +@property (assign, nonatomic, readwrite) BOOL supportsSubscriptions; +@property (strong, nonatomic) NSMutableDictionary *> *capabilityObservers; +@property (strong, nonatomic) NSMutableDictionary *> *subscriptionStatus; + +@property (nullable, strong, nonatomic) SDLSystemCapability *lastReceivedCapability; + +@property (assign, nonatomic) BOOL shouldConvertDeprecatedDisplayCapabilities; +@property (strong, nonatomic) SDLHMILevel currentHMILevel; + @end QuickSpecBegin(SDLSystemCapabilityManagerSpec) -fdescribe(@"System capability manager", ^{ +describe(@"System capability manager", ^{ __block SDLSystemCapabilityManager *testSystemCapabilityManager = nil; __block TestConnectionManager *testConnectionManager = nil; @@ -157,7 +172,7 @@ @interface SDLSystemCapabilityManager () expect(testSystemCapabilityManager.remoteControlCapability).to(beNil()); expect(testSystemCapabilityManager.appServicesCapabilities).to(beNil()); expect(testSystemCapabilityManager.seatLocationCapability).to(beNil()); - + expect(testSystemCapabilityManager.currentHMILevel).to(equal(SDLHMILevelNone)); }); describe(@"isCapabilitySupported method should work correctly", ^{ @@ -520,7 +535,7 @@ @interface SDLSystemCapabilityManager () }); }); - context(@"When sending a updateCapabilityType request", ^{ + context(@"When sending a updateCapabilityType request in HMI FULL", ^{ __block SDLGetSystemCapabilityResponse *testGetSystemCapabilityResponse = nil; __block SDLPhoneCapability *testPhoneCapability = nil; @@ -531,6 +546,8 @@ @interface SDLSystemCapabilityManager () testGetSystemCapabilityResponse.systemCapability = [[SDLSystemCapability alloc] init]; testGetSystemCapabilityResponse.systemCapability.phoneCapability = testPhoneCapability; testGetSystemCapabilityResponse.systemCapability.systemCapabilityType = SDLSystemCapabilityTypePhoneCall; + + testSystemCapabilityManager.currentHMILevel = SDLHMILevelFull; }); context(@"If the request failed with an error", ^{ @@ -594,10 +611,12 @@ @interface SDLSystemCapabilityManager () }); }); - describe(@"updating the SCM through OnSystemCapability", ^{ + describe(@"updating the SCM through OnSystemCapability in HMI Full", ^{ __block SDLPhoneCapability *phoneCapability = nil; beforeEach(^{ + testSystemCapabilityManager.currentHMILevel = SDLHMILevelFull; + phoneCapability = [[SDLPhoneCapability alloc] initWithDialNumber:YES]; SDLSystemCapability *newCapability = [[SDLSystemCapability alloc] initWithPhoneCapability:phoneCapability]; SDLOnSystemCapabilityUpdated *update = [[SDLOnSystemCapabilityUpdated alloc] initWithSystemCapability:newCapability]; @@ -611,7 +630,7 @@ @interface SDLSystemCapabilityManager () }); }); - describe(@"subscribing to capability types", ^{ + describe(@"subscribing to capability types when HMI is full", ^{ __block TestSystemCapabilityObserver *phoneObserver = nil; __block TestSystemCapabilityObserver *navigationObserver = nil; __block TestSystemCapabilityObserver *videoStreamingObserver = nil; @@ -621,6 +640,8 @@ @interface SDLSystemCapabilityManager () __block NSUInteger handlerTriggeredCount = 0; beforeEach(^{ + testSystemCapabilityManager.currentHMILevel = SDLHMILevelFull; + observerTriggeredCount = 0; handlerTriggeredCount = 0; [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"5.1.0"]; // supports subscriptions From 5fa9bb42d0b6cca3ebd483734558a6a7512a5bab Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 18 Feb 2020 14:03:46 -0500 Subject: [PATCH 33/39] Remove "outside" GetSystemCapability response caching --- SmartDeviceLink/SDLSystemCapabilityManager.m | 21 -------------------- 1 file changed, 21 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 9d7301c3c..f407145fa 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -72,8 +72,6 @@ @interface SDLSystemCapabilityManager () @property (strong, nonatomic) NSMutableDictionary *> *capabilityObservers; @property (strong, nonatomic) NSMutableDictionary *> *subscriptionStatus; -@property (nullable, strong, nonatomic) SDLSystemCapability *lastReceivedCapability; - @property (assign, nonatomic) BOOL shouldConvertDeprecatedDisplayCapabilities; @property (strong, nonatomic) SDLHMILevel currentHMILevel; @@ -394,12 +392,6 @@ - (void)sdl_sendGetSystemCapabilityWithType:(SDLSystemCapabilityType)type subscr - (BOOL)sdl_saveSystemCapability:(nullable SDLSystemCapability *)systemCapability error:(nullable NSError *)error completionHandler:(nullable SDLCapabilityUpdateWithErrorHandler)handler { SDLLogV(@"Saving system capability type: %@", systemCapability); - // If this is equal to the last received capability (e.g. a notification and GetSystemCapabilityResponse), don't save twice or call the observer twice. - if ([self.lastReceivedCapability isEqual:systemCapability]) { - return NO; - } - self.lastReceivedCapability = systemCapability; - SDLSystemCapabilityType systemCapabilityType = systemCapability.systemCapabilityType; if ([systemCapabilityType isEqualToEnum:SDLSystemCapabilityTypePhoneCall]) { @@ -708,7 +700,6 @@ - (void)sdl_registerForNotifications { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_registerResponse:) name:SDLDidReceiveRegisterAppInterfaceResponse object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_displayLayoutResponse:) name:SDLDidReceiveSetDisplayLayoutResponse object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_systemCapabilityUpdatedNotification:) name:SDLDidReceiveSystemCapabilityUpdatedNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_systemCapabilityResponseNotification:) name:SDLDidReceiveGetSystemCapabilitiesResponse object:nil]; } /** @@ -788,18 +779,6 @@ - (void)sdl_systemCapabilityUpdatedNotification:(SDLRPCNotificationNotification [self sdl_saveSystemCapability:systemCapabilityUpdatedNotification.systemCapability error:nil completionHandler:nil]; } -/** - Called with a `GetSystemCapabilityResponse` notification is received from core. The updated system capability is saved. - - @param notification The `GetSystemCapabilityResponse` notification received from Core - */ -- (void)sdl_systemCapabilityResponseNotification:(SDLRPCResponseNotification *)notification { - SDLGetSystemCapabilityResponse *systemCapabilityResponse = (SDLGetSystemCapabilityResponse *)notification.response; - SDLLogV(@"Received GetSystemCapability response for type %@", systemCapabilityResponse.systemCapability.systemCapabilityType); - - [self sdl_saveSystemCapability:systemCapabilityResponse.systemCapability error:nil completionHandler:nil]; -} - - (void)sdl_hmiStatusNotification:(SDLRPCNotificationNotification *)notification { SDLOnHMIStatus *onHMIStatus = (SDLOnHMIStatus *)notification.notification; self.currentHMILevel = onHMIStatus.hmiLevel; From 1b5da0abd131d75bbe35da96cfde928ff3f8307f Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 18 Feb 2020 14:18:18 -0500 Subject: [PATCH 34/39] Fix tests after removing GSCR auto-caching --- .../SDLSystemCapabilityManagerSpec.m | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m index 41cf72929..5ce732c86 100644 --- a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m +++ b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m @@ -656,7 +656,7 @@ @interface SDLSystemCapabilityManager () [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypeDisplays withObserver:displaysObserver selector:@selector(capabilityUpdatedWithCapability:error:subscribed:)]; }); - describe(@"when observers aren't supported", ^{ + context(@"when observers aren't supported", ^{ __block BOOL observationSuccess = NO; beforeEach(^{ @@ -693,11 +693,11 @@ @interface SDLSystemCapabilityManager () [[NSNotificationCenter defaultCenter] postNotification:notification]; }); - it(@"should notify subscribers of the new data", ^{ - expect(handlerTriggeredCount).toEventually(equal(2)); - expect(observerTriggeredCount).toEventually(equal(2)); + it(@"should not notify subscribers of new data because it was sent outside of the SCM", ^{ + expect(handlerTriggeredCount).toEventually(equal(1)); + expect(observerTriggeredCount).toEventually(equal(1)); - expect(phoneObserver.selectorCalledCount).toEventually(equal(2)); + expect(phoneObserver.selectorCalledCount).toEventually(equal(1)); expect(navigationObserver.selectorCalledCount).toEventually(equal(1)); @@ -724,10 +724,10 @@ @interface SDLSystemCapabilityManager () }); it(@"should not notify the subscriber of the new data", ^{ - expect(handlerTriggeredCount).toEventually(equal(2)); - expect(observerTriggeredCount).toEventually(equal(2)); + expect(handlerTriggeredCount).toEventually(equal(1)); + expect(observerTriggeredCount).toEventually(equal(1)); - expect(phoneObserver.selectorCalledCount).toEventually(equal(2)); // No change from above + expect(phoneObserver.selectorCalledCount).toEventually(equal(1)); // No change from above expect(navigationObserver.selectorCalledCount).toEventually(equal(1)); From 086709519df918b7a6ab99f1fd6367cc4601cbe2 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 19 Feb 2020 14:38:30 -0500 Subject: [PATCH 35/39] SCM fixes * Fix HMI status is never updated * Fix HMI NONE error is now propagated to the observer --- SmartDeviceLink/SDLSystemCapabilityManager.m | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index f407145fa..d2cf5eb6b 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -507,13 +507,14 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n - (nullable id)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; } - SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:[[NSObject alloc] init] block:block]; 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 array]; @@ -533,13 +534,14 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n - (nullable id)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; } - SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:[[NSObject alloc] init] updateHandler:handler]; 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 array]; @@ -565,17 +567,18 @@ - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id return NO; } - 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); - return NO; - } - if (observer == nil) { SDLLogE(@"Attempted to subscribe to type: %@ with a selector on a *nil* observer, which will always fail.", type); return NO; } SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:observer selector:selector]; + 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; + } + 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 array]; @@ -700,6 +703,7 @@ - (void)sdl_registerForNotifications { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_registerResponse:) name:SDLDidReceiveRegisterAppInterfaceResponse object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_displayLayoutResponse:) name:SDLDidReceiveSetDisplayLayoutResponse object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_systemCapabilityUpdatedNotification:) name:SDLDidReceiveSystemCapabilityUpdatedNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_hmiStatusNotification:) name:SDLDidChangeHMIStatusNotification object:nil]; } /** From b406d1525147f6894cd480cf937d665d33b793eb Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 20 Feb 2020 10:29:30 -0500 Subject: [PATCH 36/39] Update subscriptions to not return a cached value if a GSC will be sent --- SmartDeviceLink/SDLSystemCapabilityManager.m | 39 ++++++++++++-------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index d2cf5eb6b..d248da7ec 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -517,17 +517,20 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n 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 array]; + 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]; } - [self.capabilityObservers[type] addObject:observerObject]; - - // Call the block immediately with the cached value - [self sdl_invokeObserver:observerObject withCapabilityType:type capability:[self sdl_cachedCapabilityForType:type] error:nil]; return observerObject.observer; } @@ -544,17 +547,20 @@ - (void)sdl_saveDisplayCapabilityListUpdate:(NSArray *)n 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 array]; + 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]; } - [self.capabilityObservers[type] addObject:observerObject]; - - // Call the block immediately with the cached value - [self sdl_invokeObserver:observerObject withCapabilityType:type capability:[self sdl_cachedCapabilityForType:type] error:nil]; return observerObject.observer; } @@ -581,18 +587,21 @@ - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id 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 array]; + 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]; } - // 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 YES; } From dbc5d4fb1dcf31a0662e9ed2b02a5bab3b2a2c45 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 20 Feb 2020 13:38:09 -0500 Subject: [PATCH 37/39] Remove untrue documentation --- SmartDeviceLink/SDLSystemCapabilityManager.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.h b/SmartDeviceLink/SDLSystemCapabilityManager.h index 36c55799a..237ed08d8 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.h +++ b/SmartDeviceLink/SDLSystemCapabilityManager.h @@ -276,7 +276,7 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla /// This method will be called immediately with the current value and will be called every time the value is updated. If this is the first subscription of this `SDLSystemCapabilityType`, then the current cached value of `nil` will be returned immediately, an updated value will be retrieved, and a subscription will be attempted. /// /// On sub-v5.1.0 systems (where `supportsSubscriptions == NO`): -/// The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the current cached value of `nil` will be returned immediately, an updated value will be retrieved, and a subscription will be attempted. +/// The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and returned. /// /// @param type The type of capability to subscribe to /// @param block The block to be called when the capability is updated @@ -291,7 +291,7 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla /// Note that when the cached value is returned, the `subscribed` flag on the handler will be false until the subscription completes successfully and a new value is retrieved. /// /// On sub-v5.1.0 systems (where `supportsSubscriptions == NO`): -/// The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and returned. The current cached value (`nil`) will nevertheless be returned immediately. +/// The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and returned. /// @param type The type of capability to subscribe to /// @param handler The block to be called when the capability is updated with an error if one occurs @@ -315,7 +315,7 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla * This method will be called immediately with the current value and will be called every time the value is updated. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and a subscription will be attempted. The current cached value (`nil`) will nevertheless be returned immediately. * * On sub-v5.1.0 systems (where `supportsSubscriptions == NO`): - * The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and returned. The current cached value (`nil`) will nevertheless be returned immediately. + * The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and returned. * * @param type The type of the system capability to subscribe to * @param observer The object that will have `selector` called whenever the capability is updated From 6dbcb42d2e39149cd6fe2d6f197e47e4f6436686 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 20 Feb 2020 14:43:39 -0500 Subject: [PATCH 38/39] Fix incorrect documentation --- SmartDeviceLink/SDLSystemCapabilityManager.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.h b/SmartDeviceLink/SDLSystemCapabilityManager.h index 237ed08d8..49eb4da37 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.h +++ b/SmartDeviceLink/SDLSystemCapabilityManager.h @@ -273,7 +273,7 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla /// Subscribe to a particular capability type using a block callback. /// /// On v5.1.0+ systems (where `supportsSubscriptions == YES`): -/// This method will be called immediately with the current value and will be called every time the value is updated. If this is the first subscription of this `SDLSystemCapabilityType`, then the current cached value of `nil` will be returned immediately, an updated value will be retrieved, and a subscription will be attempted. +/// This method will be called immediately with the current value if a subscription already exists and will be called every time the value is updated. /// /// On sub-v5.1.0 systems (where `supportsSubscriptions == NO`): /// The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and returned. @@ -286,7 +286,7 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla /// Subscribe to a particular capability type using a handler callback. /// /// On v5.1.0+ systems (where `supportsSubscriptions == YES`): -/// This method will be called immediately with the current value and will be called every time the value is updated. If this is the first subscription of this `SDLSystemCapabilityType`, then the current cached value of `nil` will be returned immediately, an updated value will be retrieved, and a subscription will be attempted. +/// This method will be called immediately with the current value if a subscription already exists and will be called every time the value is updated. /// /// Note that when the cached value is returned, the `subscribed` flag on the handler will be false until the subscription completes successfully and a new value is retrieved. /// @@ -312,7 +312,7 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla * 4. Three parameters, one `SDLSystemCapability *` parameter, one `NSError` parameter, and one `BOOL` parameter e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability error:(NSError *)error subscribed:(BOOL)subscribed` * * On v5.1.0+ systems (where `supportsSubscriptions == YES`): - * This method will be called immediately with the current value and will be called every time the value is updated. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and a subscription will be attempted. The current cached value (`nil`) will nevertheless be returned immediately. + * This method will be called immediately with the current value if a subscription already exists and will be called every time the value is updated. * * On sub-v5.1.0 systems (where `supportsSubscriptions == NO`): * The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and returned. From bf96806ffc74cab14f58395a466cd41fdfee3196 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 20 Feb 2020 14:51:59 -0500 Subject: [PATCH 39/39] Last documentation fixes --- SmartDeviceLink/SDLSystemCapabilityManager.h | 59 ++++++++++---------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.h b/SmartDeviceLink/SDLSystemCapabilityManager.h index 49eb4da37..ca1c35792 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.h +++ b/SmartDeviceLink/SDLSystemCapabilityManager.h @@ -292,44 +292,41 @@ typedef void (^SDLCapabilityUpdateWithErrorHandler)(SDLSystemCapability * _Nulla /// /// On sub-v5.1.0 systems (where `supportsSubscriptions == NO`): /// The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and returned. - +/// /// @param type The type of capability to subscribe to /// @param handler The block to be called when the capability is updated with an error if one occurs /// @return An object that can be used to unsubscribe the block using unsubscribeFromCapabilityType:withObserver: by passing it in the observer callback, or nil if the manager can't attempt the subscription for some reason (such as the app being in HMI_NONE and the type is not DISPLAYS). - (nullable id)subscribeToCapabilityType:(SDLSystemCapabilityType)type withUpdateHandler:(SDLCapabilityUpdateWithErrorHandler)handler NS_SWIFT_NAME(subscribe(capabilityType:updateHandler:)); -/** - * Subscribe to a particular capability type with a selector callback. - * - * The selector supports the following parameters: - * - * 1. No parameters e.g. `- (void)phoneCapabilityUpdated;` - * - * 2. One `SDLSystemCapability *` parameter, e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability` - * - * 3. Two parameters, one `SDLSystemCapability *` parameter, and one `NSError` parameter, e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability error:(NSError *)error` - * - * 4. Three parameters, one `SDLSystemCapability *` parameter, one `NSError` parameter, and one `BOOL` parameter e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability error:(NSError *)error subscribed:(BOOL)subscribed` - * - * On v5.1.0+ systems (where `supportsSubscriptions == YES`): - * This method will be called immediately with the current value if a subscription already exists and will be called every time the value is updated. - * - * On sub-v5.1.0 systems (where `supportsSubscriptions == NO`): - * The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and returned. - * - * @param type The type of the system capability to subscribe to - * @param observer The object that will have `selector` called whenever the capability is updated - * @param selector The selector on `observer` that will be called whenever the capability is updated - * @return YES if the manager is attempting the subscription, or NO if the manager can't attempt the subscription for some reason (such as the app being in HMI_NONE and the type is not DISPLAYS), or the selector doesn't contain the correct number of parameters. - */ + +/// Subscribe to a particular capability type with a selector callback. +/// +/// The selector supports the following parameters: +/// +/// 1. No parameters e.g. `- (void)phoneCapabilityUpdated;` +/// +/// 2. One `SDLSystemCapability *` parameter, e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability` +/// +/// 3. Two parameters, one `SDLSystemCapability *` parameter, and one `NSError` parameter, e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability error:(NSError *)error` +/// +/// 4. Three parameters, one `SDLSystemCapability *` parameter, one `NSError` parameter, and one `BOOL` parameter e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability error:(NSError *)error subscribed:(BOOL)subscribed` +/// +/// On v5.1.0+ systems (where `supportsSubscriptions == YES`): +/// This method will be called immediately with the current value if a subscription already exists and will be called every time the value is updated. +/// +/// On sub-v5.1.0 systems (where `supportsSubscriptions == NO`): +/// The method will be called immediately with the current value and will _not_ be automatically called every time the value is updated, unless the `type` is `DISPLAYS` which is supported on every version. If `updateCapabilityType:completionHandler` is called and a new value is retrieved, this value will be updated then. If this is the first subscription of this `SDLSystemCapabilityType`, then the value will be retrieved and returned. +/// +/// @param type The type of the system capability to subscribe to +/// @param observer The object that will have `selector` called whenever the capability is updated +/// @param selector The selector on `observer` that will be called whenever the capability is updated +/// @return YES if the manager is attempting the subscription, or NO if the manager can't attempt the subscription for some reason (such as the app being in HMI_NONE and the type is not DISPLAYS), or the selector doesn't contain the correct number of parameters. - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id)observer selector:(SEL)selector; -/** - * Unsubscribe from a particular capability type. If it was subscribed with a block / handler, the return value should be passed to the `observer` to unsubscribe the block. If it was subscribed with a selector, the `observer` object (on which the selector exists and is called) should be passed to unsubscribe the object selector. - * - * @param type The type of the system capability to unsubscribe from - * @param observer The object that will be unsubscribed. If a block was subscribed, the return value should be passed. If a selector was subscribed, the observer object should be passed. - */ +/// Unsubscribe from a particular capability type. If it was subscribed with a block / handler, the return value should be passed to the `observer` to unsubscribe the block. If it was subscribed with a selector, the `observer` object (on which the selector exists and is called) should be passed to unsubscribe the object selector. +/// +/// @param type The type of the system capability to unsubscribe from +/// @param observer The object that will be unsubscribed. If a block was subscribed, the return value should be passed. If a selector was subscribed, the observer object should be passed. - (void)unsubscribeFromCapabilityType:(SDLSystemCapabilityType)type withObserver:(id)observer; @end