From e1b32315de632ef44df667a8d277ccbf1ed42fc5 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 6 Nov 2017 16:16:07 -0500 Subject: [PATCH 1/6] Fix assert after registration. // FREEBIE --- SignalServiceKit/src/Messages/OWSMessageSender.m | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 7ba8024a5..9ac276096 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -978,7 +978,16 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // of new secondary devices when this message send fails. DDLogWarn(@"%@ sync message has no device messages but account has secondary devices.", self.logTag); } else if (hasDeviceMessages) { - OWSFail(@"%@ sync message has device messages for unknown secondary devices.", self.logTag); + for (NSDictionary *deviceMessage in deviceMessages) { + NSNumber *_Nullable deviceId = deviceMessage[@"destinationDeviceId"]; + if (deviceId && [deviceId isKindOfClass:[NSNumber class]] && [deviceId isEqualToNumber:@(1)]) { + DDLogWarn(@"%@ trying to sending sync message to primary device, presumably because user has just " + @"registered.", + self.logTag); + } else { + OWSFail(@"%@ sync message has device messages for unknown secondary devices.", self.logTag); + } + } } else { // Account has secondary devices; proceed as usual. } From 8b65246619e604fd12f8edd7786c9685629f5472 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 7 Nov 2017 14:42:32 -0500 Subject: [PATCH 2/6] Respond to CR. // FREEBIE --- .../src/Contacts/SignalRecipient.h | 2 +- .../src/Contacts/SignalRecipient.m | 18 +++++++++++++++++- .../src/Messages/OWSMessageSender.m | 11 +---------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/SignalServiceKit/src/Contacts/SignalRecipient.h b/SignalServiceKit/src/Contacts/SignalRecipient.h index 39b3bd44f..2151829d9 100644 --- a/SignalServiceKit/src/Contacts/SignalRecipient.h +++ b/SignalServiceKit/src/Contacts/SignalRecipient.h @@ -21,7 +21,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)removeDevices:(NSSet *)set; @property (nonatomic, nullable) NSString *relay; -@property (nonatomic, retain) NSMutableOrderedSet *devices; +@property (nonatomic) NSMutableOrderedSet *devices; - (BOOL)supportsVoice; // This property indicates support for both WebRTC audio and video calls. diff --git a/SignalServiceKit/src/Contacts/SignalRecipient.m b/SignalServiceKit/src/Contacts/SignalRecipient.m index dc71f3a01..3e08e4558 100644 --- a/SignalServiceKit/src/Contacts/SignalRecipient.m +++ b/SignalServiceKit/src/Contacts/SignalRecipient.m @@ -23,7 +23,23 @@ NS_ASSUME_NONNULL_BEGIN return self; } - _devices = [NSMutableOrderedSet orderedSetWithObject:[NSNumber numberWithInt:1]]; + OWSAssert([TSAccountManager localNumber].length > 0); + if ([[TSAccountManager localNumber] isEqualToString:textSecureIdentifier]) { + // Default to no devices. + // + // This instance represents our own account and is used for sending + // sync message to linked devices. We shouldn't have any linked devices + // yet when we create the "self" SignalRecipient, and we don't need to + // send sync messages to the primary - we ARE the primary. + _devices = [NSMutableOrderedSet new]; + } else { + // Default to sending to just primary device. + // + // OWSMessageSender will correct this if it is wrong the next time + // we send a message to this recipient. + _devices = [NSMutableOrderedSet orderedSetWithObject:@(1)]; + } + _relay = [relay isEqualToString:@""] ? nil : relay; return self; diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 9ac276096..7ba8024a5 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -978,16 +978,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // of new secondary devices when this message send fails. DDLogWarn(@"%@ sync message has no device messages but account has secondary devices.", self.logTag); } else if (hasDeviceMessages) { - for (NSDictionary *deviceMessage in deviceMessages) { - NSNumber *_Nullable deviceId = deviceMessage[@"destinationDeviceId"]; - if (deviceId && [deviceId isKindOfClass:[NSNumber class]] && [deviceId isEqualToNumber:@(1)]) { - DDLogWarn(@"%@ trying to sending sync message to primary device, presumably because user has just " - @"registered.", - self.logTag); - } else { - OWSFail(@"%@ sync message has device messages for unknown secondary devices.", self.logTag); - } - } + OWSFail(@"%@ sync message has device messages for unknown secondary devices.", self.logTag); } else { // Account has secondary devices; proceed as usual. } From 071dbd441439cfedc74eaf7f3e7bc79ba730d374 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 7 Nov 2017 14:52:31 -0500 Subject: [PATCH 3/6] Respond to CR. // FREEBIE --- .../src/Messages/OWSMessageSender.m | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 7ba8024a5..95051e372 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -954,11 +954,29 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; NSString *localNumber = [TSAccountManager localNumber]; if ([localNumber isEqualToString:recipient.uniqueId]) { + OWSAssert([message isKindOfClass:[OWSOutgoingSyncMessage class]]); + // Messages send to the "local number" should be sync messages. + // + // We can skip sending sync messages if we know that we have no linked + // devices. However, we need to be sure to handle the case where the + // linked device list has just changed. + // + // The linked device list is reflected in two separate pieces of state: + // + // * OWSDevice's state is updated when you link or unlink a device. + // * SignalRecipient's state is updated by 409 "Mismatched devices" + // responses from the service. + // + // If _both_ of these pieces of state agree that there are no linked + // devices, then can safely skip sending sync message. + + // 1. Check OWSDevice's state. __block BOOL hasSecondaryDevices; [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { hasSecondaryDevices = [OWSDevice hasSecondaryDevicesWithTransaction:transaction]; }]; + // 2. Check SignalRecipient's state. BOOL hasDeviceMessages = deviceMessages.count > 0; if (!hasSecondaryDevices && !hasDeviceMessages) { @@ -975,7 +993,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } else if (hasSecondaryDevices) { // We may have just linked a new secondary device which is not yet reflected in // the SignalRecipient that corresponds to ourself. Proceed. Client should learn - // of new secondary devices when this message send fails. + // of new secondary devices via 409 "Mismatched devices" response. DDLogWarn(@"%@ sync message has no device messages but account has secondary devices.", self.logTag); } else if (hasDeviceMessages) { OWSFail(@"%@ sync message has device messages for unknown secondary devices.", self.logTag); From efcd42012cd6d9aaaafeef4c547602ee1284c1e5 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 10 Nov 2017 11:04:40 -0500 Subject: [PATCH 4/6] Respond to CR. // FREEBIE --- .../OWSLinkDeviceViewController.m | 13 ++- .../OWSLinkedDevicesTableViewController.m | 17 ++-- SignalServiceKit/src/Devices/OWSDevice.h | 11 ++- SignalServiceKit/src/Devices/OWSDevice.m | 86 ++++++++++++------- .../src/Messages/OWSMessageSender.m | 16 ++-- .../src/Storage/YapDatabaseConnection+OWS.h | 1 + .../src/Storage/YapDatabaseConnection+OWS.m | 5 ++ 7 files changed, 101 insertions(+), 48 deletions(-) diff --git a/Signal/src/ViewControllers/OWSLinkDeviceViewController.m b/Signal/src/ViewControllers/OWSLinkDeviceViewController.m index 52da44807..edc941136 100644 --- a/Signal/src/ViewControllers/OWSLinkDeviceViewController.m +++ b/Signal/src/ViewControllers/OWSLinkDeviceViewController.m @@ -9,6 +9,7 @@ #import "OWSProfileManager.h" #import "Signal-Swift.h" #import +#import #import #import #import @@ -18,9 +19,10 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSLinkDeviceViewController () -@property (strong, nonatomic) IBOutlet UIView *qrScanningView; -@property (strong, nonatomic) IBOutlet UILabel *scanningInstructionsLabel; -@property (strong, nonatomic) OWSQRCodeScanningViewController *qrScanningController; +@property (nonatomic) YapDatabaseConnection *dbConnection; +@property (nonatomic) IBOutlet UIView *qrScanningView; +@property (nonatomic) IBOutlet UILabel *scanningInstructionsLabel; +@property (nonatomic) OWSQRCodeScanningViewController *qrScanningController; @property (nonatomic, readonly) OWSReadReceiptManager *readReceiptManager; @end @@ -31,6 +33,8 @@ NS_ASSUME_NONNULL_BEGIN { [super viewDidLoad]; + self.dbConnection = [[TSStorageManager sharedManager] newDatabaseConnection]; + // HACK to get full width preview layer CGRect oldFrame = self.qrScanningView.frame; self.qrScanningView.frame = CGRectMake( @@ -142,6 +146,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)provisionWithParser:(OWSDeviceProvisioningURLParser *)parser { + // Optimistically set this flag. + [OWSDevice setMayHaveLinkedDevices:YES dbConnection:self.dbConnection]; + ECKeyPair *_Nullable identityKeyPair = [[OWSIdentityManager sharedManager] identityKeyPair]; OWSAssert(identityKeyPair); NSData *myPublicKey = identityKeyPair.publicKey; diff --git a/Signal/src/ViewControllers/OWSLinkedDevicesTableViewController.m b/Signal/src/ViewControllers/OWSLinkedDevicesTableViewController.m index ebd4490e5..9b5efc150 100644 --- a/Signal/src/ViewControllers/OWSLinkedDevicesTableViewController.m +++ b/Signal/src/ViewControllers/OWSLinkedDevicesTableViewController.m @@ -20,10 +20,10 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSLinkedDevicesTableViewController () -@property YapDatabaseConnection *dbConnection; -@property YapDatabaseViewMappings *deviceMappings; -@property NSTimer *pollingRefreshTimer; -@property BOOL isExpectingMoreDevices; +@property (nonatomic) YapDatabaseConnection *dbConnection; +@property (nonatomic) YapDatabaseViewMappings *deviceMappings; +@property (nonatomic) NSTimer *pollingRefreshTimer; +@property (nonatomic) BOOL isExpectingMoreDevices; @end @@ -131,6 +131,14 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1; __weak typeof(self) wself = self; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [[OWSDevicesService new] getDevicesWithSuccess:^(NSArray *devices) { + // If we have more than one device; we may have a linked device. + if (devices.count > 1) { + // Setting this flag here shouldn't be necessary, but we do so + // because the "cost" is low and it will improve robustness. + [OWSDevice setMayHaveLinkedDevices:YES + dbConnection:[[TSStorageManager sharedManager] newDatabaseConnection]]; + } + if (devices.count > [OWSDevice numberOfKeysInCollection]) { // Got our new device, we can stop refreshing. wself.isExpectingMoreDevices = NO; @@ -243,7 +251,6 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1; return (NSInteger)[self.deviceMappings numberOfItemsInSection:(NSUInteger)section]; case OWSLinkedDevicesTableViewControllerSectionAddDevice: return 1; - default: DDLogError(@"Unknown section: %ld", (long)section); return 0; diff --git a/SignalServiceKit/src/Devices/OWSDevice.h b/SignalServiceKit/src/Devices/OWSDevice.h index ce3230a57..095f04909 100644 --- a/SignalServiceKit/src/Devices/OWSDevice.h +++ b/SignalServiceKit/src/Devices/OWSDevice.h @@ -12,9 +12,9 @@ extern uint32_t const OWSDevicePrimaryDeviceId; @interface OWSDevice : TSYapDatabaseObject @property (nonatomic, readonly) NSInteger deviceId; -@property (nullable, readonly) NSString *name; -@property (readonly) NSDate *createdAt; -@property (readonly) NSDate *lastSeenAt; +@property (nonatomic, readonly, nullable) NSString *name; +@property (nonatomic, readonly) NSDate *createdAt; +@property (nonatomic, readonly) NSDate *lastSeenAt; + (instancetype)deviceFromJSONDictionary:(NSDictionary *)deviceAttributes error:(NSError **)error; @@ -52,6 +52,11 @@ extern uint32_t const OWSDevicePrimaryDeviceId; */ - (BOOL)updateAttributesWithDevice:(OWSDevice *)other; +#pragma mark - "May Have Linked Devices" Flag + ++ (BOOL)mayHaveLinkedDevices:(YapDatabaseConnection *)dbConnection; ++ (void)setMayHaveLinkedDevices:(BOOL)value dbConnection:(YapDatabaseConnection *)dbConnection; + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Devices/OWSDevice.m b/SignalServiceKit/src/Devices/OWSDevice.m index b6a7be014..886cbc5ae 100644 --- a/SignalServiceKit/src/Devices/OWSDevice.m +++ b/SignalServiceKit/src/Devices/OWSDevice.m @@ -5,26 +5,29 @@ #import "OWSDevice.h" #import "NSDate+OWS.h" #import "OWSError.h" +#import "TSStorageManager.h" #import "YapDatabaseConnection.h" #import "YapDatabaseTransaction.h" #import NS_ASSUME_NONNULL_BEGIN -static MTLValueTransformer *_millisecondTimestampToDateTransformer; uint32_t const OWSDevicePrimaryDeviceId = 1; +NSString *const kTSStorageManager_OWSDeviceCollection = @"kTSStorageManager_OWSDeviceCollection"; +NSString *const kTSStorageManager_MayHaveLinkedDevices = @"kTSStorageManager_MayHaveLinkedDevices"; @interface OWSDevice () -@property NSString *name; -@property NSDate *createdAt; -@property NSDate *lastSeenAt; +@property (nonatomic) NSInteger deviceId; +@property (nonatomic, nullable) NSString *name; +@property (nonatomic) NSDate *createdAt; +@property (nonatomic) NSDate *lastSeenAt; @end -@implementation OWSDevice +#pragma mark - -@synthesize name = _name; +@implementation OWSDevice + (instancetype)deviceFromJSONDictionary:(NSDictionary *)deviceAttributes error:(NSError **)error { @@ -76,38 +79,39 @@ uint32_t const OWSDevicePrimaryDeviceId = 1; + (MTLValueTransformer *)millisecondTimestampToDateTransformer { - if (!_millisecondTimestampToDateTransformer) { - _millisecondTimestampToDateTransformer = - [MTLValueTransformer transformerUsingForwardBlock:^id(id value, BOOL *success, NSError **error) { - if ([value isKindOfClass:[NSNumber class]]) { - NSNumber *number = (NSNumber *)value; - NSDate *result = [NSDate ows_dateWithMillisecondsSince1970:[number longLongValue]]; + static MTLValueTransformer *instance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [MTLValueTransformer transformerUsingForwardBlock:^id(id value, BOOL *success, NSError **error) { + if ([value isKindOfClass:[NSNumber class]]) { + NSNumber *number = (NSNumber *)value; + NSDate *result = [NSDate ows_dateWithMillisecondsSince1970:[number longLongValue]]; + if (result) { + *success = YES; + return result; + } + } + *success = NO; + DDLogError(@"%@ unable to decode date from %@", self.logTag, value); + *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecodeJson, @"Unable to decode date from %@"); + return nil; + } + reverseBlock:^id(id value, BOOL *success, NSError **error) { + if ([value isKindOfClass:[NSDate class]]) { + NSDate *date = (NSDate *)value; + NSNumber *result = [NSNumber numberWithLongLong:[NSDate ows_millisecondsSince1970ForDate:date]]; if (result) { *success = YES; return result; } } + DDLogError(@"%@ unable to encode date from %@", self.logTag, value); + *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToEncodeJson, @"Unable to encode date"); *success = NO; - DDLogError(@"%@ unable to decode date from %@", self.logTag, value); - *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecodeJson, @"Unable to decode date from %@"); return nil; - } - reverseBlock:^id(id value, BOOL *success, NSError **error) { - if ([value isKindOfClass:[NSDate class]]) { - NSDate *date = (NSDate *)value; - NSNumber *result = [NSNumber numberWithLongLong:[NSDate ows_millisecondsSince1970ForDate:date]]; - if (result) { - *success = YES; - return result; - } - } - DDLogError(@"%@ unable to encode date from %@", self.logTag, value); - *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToEncodeJson, @"Unable to encode date"); - *success = NO; - return nil; - }]; - } - return _millisecondTimestampToDateTransformer; + }]; + }); + return instance; } + (uint32_t)currentDeviceId @@ -178,6 +182,26 @@ uint32_t const OWSDevicePrimaryDeviceId = 1; return self.deviceId == device.deviceId; } +#pragma mark - "May Have Linked Devices" Flag + ++ (BOOL)mayHaveLinkedDevices:(YapDatabaseConnection *)dbConnection +{ + OWSAssert(dbConnection); + + return [dbConnection boolForKey:kTSStorageManager_MayHaveLinkedDevices + inCollection:kTSStorageManager_OWSDeviceCollection + defaultValue:YES]; +} + ++ (void)setMayHaveLinkedDevices:(BOOL)value dbConnection:(YapDatabaseConnection *)dbConnection +{ + OWSAssert(dbConnection); + + [dbConnection setBool:value + forKey:kTSStorageManager_MayHaveLinkedDevices + inCollection:kTSStorageManager_OWSDeviceCollection]; +} + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 95051e372..ba7a27a28 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -971,15 +971,12 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // devices, then can safely skip sending sync message. // 1. Check OWSDevice's state. - __block BOOL hasSecondaryDevices; - [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - hasSecondaryDevices = [OWSDevice hasSecondaryDevicesWithTransaction:transaction]; - }]; + BOOL mayHaveLinkedDevices = [OWSDevice mayHaveLinkedDevices:self.dbConnection]; // 2. Check SignalRecipient's state. BOOL hasDeviceMessages = deviceMessages.count > 0; - if (!hasSecondaryDevices && !hasDeviceMessages) { + if (!mayHaveLinkedDevices && !hasDeviceMessages) { DDLogInfo(@"%@ Ignoring sync message without secondary devices: %@", self.logTag, [message class]); OWSAssert([message isKindOfClass:[OWSOutgoingSyncMessage class]]); @@ -990,7 +987,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; }); return; - } else if (hasSecondaryDevices) { + } else if (mayHaveLinkedDevices) { // We may have just linked a new secondary device which is not yet reflected in // the SignalRecipient that corresponds to ourself. Proceed. Client should learn // of new secondary devices via 409 "Mismatched devices" response. @@ -1113,6 +1110,13 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; NSArray *extraDevices = [dictionary objectForKey:@"extraDevices"]; NSArray *missingDevices = [dictionary objectForKey:@"missingDevices"]; + if (missingDevices.count > 0) { + NSString *localNumber = [TSAccountManager localNumber]; + if ([localNumber isEqualToString:recipient.uniqueId]) { + [OWSDevice setMayHaveLinkedDevices:YES dbConnection:self.dbConnection]; + } + } + dispatch_async([OWSDispatch sessionStoreQueue], ^{ if (extraDevices.count < 1 && missingDevices.count < 1) { OWSProdFail([OWSAnalyticsEvents messageSenderErrorNoMissingOrExtraDevices]); diff --git a/SignalServiceKit/src/Storage/YapDatabaseConnection+OWS.h b/SignalServiceKit/src/Storage/YapDatabaseConnection+OWS.h index d6aac59cc..79f8e6018 100644 --- a/SignalServiceKit/src/Storage/YapDatabaseConnection+OWS.h +++ b/SignalServiceKit/src/Storage/YapDatabaseConnection+OWS.h @@ -14,6 +14,7 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)hasObjectForKey:(NSString *)key inCollection:(NSString *)collection; - (BOOL)boolForKey:(NSString *)key inCollection:(NSString *)collection; +- (BOOL)boolForKey:(NSString *)key inCollection:(NSString *)collection defaultValue:(BOOL)defaultValue; - (int)intForKey:(NSString *)key inCollection:(NSString *)collection; - (nullable id)objectForKey:(NSString *)key inCollection:(NSString *)collection; - (nullable NSDate *)dateForKey:(NSString *)key inCollection:(NSString *)collection; diff --git a/SignalServiceKit/src/Storage/YapDatabaseConnection+OWS.m b/SignalServiceKit/src/Storage/YapDatabaseConnection+OWS.m index 957d0d4c8..9f2d2c024 100644 --- a/SignalServiceKit/src/Storage/YapDatabaseConnection+OWS.m +++ b/SignalServiceKit/src/Storage/YapDatabaseConnection+OWS.m @@ -51,6 +51,11 @@ NS_ASSUME_NONNULL_BEGIN } - (BOOL)boolForKey:(NSString *)key inCollection:(NSString *)collection +{ + return [self boolForKey:key inCollection:collection defaultValue:NO]; +} + +- (BOOL)boolForKey:(NSString *)key inCollection:(NSString *)collection defaultValue:(BOOL)defaultValue { NSNumber *_Nullable number = [self objectForKey:key inCollection:collection ofExpectedType:[NSNumber class]]; return [number boolValue]; From 518f15155acddf006d5647c4759e41d43bcd05ee Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 10 Nov 2017 12:04:24 -0500 Subject: [PATCH 5/6] Respond to CR. // FREEBIE --- .../OWSLinkDeviceViewController.m | 2 +- SignalServiceKit/src/Devices/OWSDevice.h | 18 +++-- SignalServiceKit/src/Devices/OWSDevice.m | 81 ++++++++++++++----- .../src/Messages/OWSMessageSender.m | 4 +- .../src/Storage/YapDatabaseConnection+OWS.m | 4 +- 5 files changed, 79 insertions(+), 30 deletions(-) diff --git a/Signal/src/ViewControllers/OWSLinkDeviceViewController.m b/Signal/src/ViewControllers/OWSLinkDeviceViewController.m index edc941136..81385f76e 100644 --- a/Signal/src/ViewControllers/OWSLinkDeviceViewController.m +++ b/Signal/src/ViewControllers/OWSLinkDeviceViewController.m @@ -147,7 +147,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)provisionWithParser:(OWSDeviceProvisioningURLParser *)parser { // Optimistically set this flag. - [OWSDevice setMayHaveLinkedDevices:YES dbConnection:self.dbConnection]; + [OWSDeviceManager.sharedManager setMayHaveLinkedDevices:YES dbConnection:self.dbConnection]; ECKeyPair *_Nullable identityKeyPair = [[OWSIdentityManager sharedManager] identityKeyPair]; OWSAssert(identityKeyPair); diff --git a/SignalServiceKit/src/Devices/OWSDevice.h b/SignalServiceKit/src/Devices/OWSDevice.h index 095f04909..eba4af55c 100644 --- a/SignalServiceKit/src/Devices/OWSDevice.h +++ b/SignalServiceKit/src/Devices/OWSDevice.h @@ -9,6 +9,19 @@ NS_ASSUME_NONNULL_BEGIN extern uint32_t const OWSDevicePrimaryDeviceId; +@interface OWSDeviceManager : NSObject + +- (instancetype)init NS_UNAVAILABLE; + ++ (instancetype)sharedManager; + +- (BOOL)mayHaveLinkedDevices:(YapDatabaseConnection *)dbConnection; +- (void)setMayHaveLinkedDevices:(BOOL)value dbConnection:(YapDatabaseConnection *)dbConnection; + +@end + +#pragma mark - + @interface OWSDevice : TSYapDatabaseObject @property (nonatomic, readonly) NSInteger deviceId; @@ -52,11 +65,6 @@ extern uint32_t const OWSDevicePrimaryDeviceId; */ - (BOOL)updateAttributesWithDevice:(OWSDevice *)other; -#pragma mark - "May Have Linked Devices" Flag - -+ (BOOL)mayHaveLinkedDevices:(YapDatabaseConnection *)dbConnection; -+ (void)setMayHaveLinkedDevices:(BOOL)value dbConnection:(YapDatabaseConnection *)dbConnection; - @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Devices/OWSDevice.m b/SignalServiceKit/src/Devices/OWSDevice.m index 886cbc5ae..d2b423535 100644 --- a/SignalServiceKit/src/Devices/OWSDevice.m +++ b/SignalServiceKit/src/Devices/OWSDevice.m @@ -16,6 +16,67 @@ uint32_t const OWSDevicePrimaryDeviceId = 1; NSString *const kTSStorageManager_OWSDeviceCollection = @"kTSStorageManager_OWSDeviceCollection"; NSString *const kTSStorageManager_MayHaveLinkedDevices = @"kTSStorageManager_MayHaveLinkedDevices"; +@interface OWSDeviceManager () + +@property (atomic, nullable) NSNumber *mayHaveLinkedDevicesCached; + +@end + +#pragma mark - + +@implementation OWSDeviceManager + ++ (instancetype)sharedManager +{ + static OWSDeviceManager *instance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [[self alloc] initDefault]; + }); + return instance; +} + +- (instancetype)initDefault +{ + return [super init]; +} + +- (BOOL)mayHaveLinkedDevices:(YapDatabaseConnection *)dbConnection +{ + OWSAssert(dbConnection); + + @synchronized(self) + { + if (!self.mayHaveLinkedDevicesCached) { + self.mayHaveLinkedDevicesCached = @([dbConnection boolForKey:kTSStorageManager_MayHaveLinkedDevices + inCollection:kTSStorageManager_OWSDeviceCollection + defaultValue:YES]); + } + + return [self.mayHaveLinkedDevicesCached boolValue]; + } +} + +- (void)setMayHaveLinkedDevices:(BOOL)value dbConnection:(YapDatabaseConnection *)dbConnection +{ + OWSAssert(dbConnection); + + @synchronized(self) + { + self.mayHaveLinkedDevicesCached = @(YES); + + [dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { + [transaction setObject:@(value) + forKey:kTSStorageManager_MayHaveLinkedDevices + inCollection:kTSStorageManager_OWSDeviceCollection]; + }]; + } +} + +@end + +#pragma mark - + @interface OWSDevice () @property (nonatomic) NSInteger deviceId; @@ -182,26 +243,6 @@ NSString *const kTSStorageManager_MayHaveLinkedDevices = @"kTSStorageManager_May return self.deviceId == device.deviceId; } -#pragma mark - "May Have Linked Devices" Flag - -+ (BOOL)mayHaveLinkedDevices:(YapDatabaseConnection *)dbConnection -{ - OWSAssert(dbConnection); - - return [dbConnection boolForKey:kTSStorageManager_MayHaveLinkedDevices - inCollection:kTSStorageManager_OWSDeviceCollection - defaultValue:YES]; -} - -+ (void)setMayHaveLinkedDevices:(BOOL)value dbConnection:(YapDatabaseConnection *)dbConnection -{ - OWSAssert(dbConnection); - - [dbConnection setBool:value - forKey:kTSStorageManager_MayHaveLinkedDevices - inCollection:kTSStorageManager_OWSDeviceCollection]; -} - @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index ba7a27a28..ad5d71c40 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -971,7 +971,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // devices, then can safely skip sending sync message. // 1. Check OWSDevice's state. - BOOL mayHaveLinkedDevices = [OWSDevice mayHaveLinkedDevices:self.dbConnection]; + BOOL mayHaveLinkedDevices = [OWSDeviceManager.sharedManager mayHaveLinkedDevices:self.dbConnection]; // 2. Check SignalRecipient's state. BOOL hasDeviceMessages = deviceMessages.count > 0; @@ -1113,7 +1113,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; if (missingDevices.count > 0) { NSString *localNumber = [TSAccountManager localNumber]; if ([localNumber isEqualToString:recipient.uniqueId]) { - [OWSDevice setMayHaveLinkedDevices:YES dbConnection:self.dbConnection]; + [OWSDeviceManager.sharedManager setMayHaveLinkedDevices:YES dbConnection:self.dbConnection]; } } diff --git a/SignalServiceKit/src/Storage/YapDatabaseConnection+OWS.m b/SignalServiceKit/src/Storage/YapDatabaseConnection+OWS.m index 9f2d2c024..0e31b973b 100644 --- a/SignalServiceKit/src/Storage/YapDatabaseConnection+OWS.m +++ b/SignalServiceKit/src/Storage/YapDatabaseConnection+OWS.m @@ -57,8 +57,8 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)boolForKey:(NSString *)key inCollection:(NSString *)collection defaultValue:(BOOL)defaultValue { - NSNumber *_Nullable number = [self objectForKey:key inCollection:collection ofExpectedType:[NSNumber class]]; - return [number boolValue]; + NSNumber *_Nullable value = [self objectForKey:key inCollection:collection ofExpectedType:[NSNumber class]]; + return value ? [value boolValue] : defaultValue; } - (nullable NSData *)dataForKey:(NSString *)key inCollection:(NSString *)collection From bac3bd4b68153dbb4587c32a307c43bed13f74ce Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 10 Nov 2017 12:57:53 -0500 Subject: [PATCH 6/6] Respond to CR. // FREEBIE --- SignalServiceKit/src/Devices/OWSDevice.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SignalServiceKit/src/Devices/OWSDevice.m b/SignalServiceKit/src/Devices/OWSDevice.m index d2b423535..2cccb3c69 100644 --- a/SignalServiceKit/src/Devices/OWSDevice.m +++ b/SignalServiceKit/src/Devices/OWSDevice.m @@ -63,7 +63,7 @@ NSString *const kTSStorageManager_MayHaveLinkedDevices = @"kTSStorageManager_May @synchronized(self) { - self.mayHaveLinkedDevicesCached = @(YES); + self.mayHaveLinkedDevicesCached = @(value); [dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { [transaction setObject:@(value)