| 
						
						
							
								
							
						
						
					 | 
					 | 
					@ -16,12 +16,13 @@ NSString *const OWSContactsManagerSignalRecipientsDidChangeNotification =
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					@interface OWSContactsManager ()
 | 
					 | 
					 | 
					 | 
					@interface OWSContactsManager ()
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					@property id addressBookReference;
 | 
					 | 
					 | 
					 | 
					@property (atomic) id addressBookReference;
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					@property TOCFuture *futureAddressBook;
 | 
					 | 
					 | 
					 | 
					@property (atomic) TOCFuture *futureAddressBook;
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					@property ObservableValueController *observableContactsController;
 | 
					 | 
					 | 
					 | 
					@property (atomic) ObservableValueController *observableContactsController;
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					@property TOCCancelTokenSource *life;
 | 
					 | 
					 | 
					 | 
					@property (atomic) TOCCancelTokenSource *life;
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					@property (atomic) NSDictionary *latestContactsById;
 | 
					 | 
					 | 
					 | 
					@property (atomic) NSDictionary *latestContactsById;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					@property (atomic) NSDictionary<NSString *, Contact *> *contactMap;
 | 
					 | 
					 | 
					 | 
					@property (atomic) NSDictionary<NSString *, Contact *> *contactMap;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					@property (nonatomic) BOOL isContactsUpdateInFlight;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					@end
 | 
					 | 
					 | 
					 | 
					@end
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -86,6 +87,7 @@ NSString *const OWSContactsManagerSignalRecipientsDidChangeNotification =
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					- (void)doAfterEnvironmentInitSetup {
 | 
					 | 
					 | 
					 | 
					- (void)doAfterEnvironmentInitSetup {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(9, 0)) {
 | 
					 | 
					 | 
					 | 
					    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(9, 0)) {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        OWSAssert(!self.contactStore);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        self.contactStore = [[CNContactStore alloc] init];
 | 
					 | 
					 | 
					 | 
					        self.contactStore = [[CNContactStore alloc] init];
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        [self.contactStore requestAccessForEntityType:CNEntityTypeContacts
 | 
					 | 
					 | 
					 | 
					        [self.contactStore requestAccessForEntityType:CNEntityTypeContacts
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                    completionHandler:^(BOOL granted, NSError *_Nullable error) {
 | 
					 | 
					 | 
					 | 
					                                    completionHandler:^(BOOL granted, NSError *_Nullable error) {
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -96,7 +98,7 @@ NSString *const OWSContactsManagerSignalRecipientsDidChangeNotification =
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                    }];
 | 
					 | 
					 | 
					 | 
					                                    }];
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    }
 | 
					 | 
					 | 
					 | 
					    }
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    [self setupAddressBook];
 | 
					 | 
					 | 
					 | 
					    [self setupAddressBookIfNecessary];
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    [self.observableContactsController watchLatestValueOnArbitraryThread:^(NSArray *latestContacts) {
 | 
					 | 
					 | 
					 | 
					    [self.observableContactsController watchLatestValueOnArbitraryThread:^(NSArray *latestContacts) {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      @synchronized(self) {
 | 
					 | 
					 | 
					 | 
					      @synchronized(self) {
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -107,9 +109,7 @@ NSString *const OWSContactsManagerSignalRecipientsDidChangeNotification =
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					- (void)verifyABPermission {
 | 
					 | 
					 | 
					 | 
					- (void)verifyABPermission {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (!self.addressBookReference) {
 | 
					 | 
					 | 
					 | 
					    [self setupAddressBookIfNecessary];
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        [self setupAddressBook];
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    }
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#pragma mark - Address Book callbacks
 | 
					 | 
					 | 
					 | 
					#pragma mark - Address Book callbacks
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -131,34 +131,78 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#pragma mark - Setup
 | 
					 | 
					 | 
					 | 
					#pragma mark - Setup
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					- (void)setupAddressBook {
 | 
					 | 
					 | 
					 | 
					- (void)setupAddressBookIfNecessary
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    @synchronized(self)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // We only need to set up our address book once;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // after that we only need to respond to onAddressBookChanged.
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        if (self.addressBookReference) {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            return;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        }
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        // De-bounce address book setup.
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        if (self.isContactsUpdateInFlight) {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            return;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        }
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        self.isContactsUpdateInFlight = YES;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    }
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    dispatch_async(ADDRESSBOOK_QUEUE, ^{
 | 
					 | 
					 | 
					 | 
					    dispatch_async(ADDRESSBOOK_QUEUE, ^{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      [[OWSContactsManager asyncGetAddressBook] thenDo:^(id addressBook) {
 | 
					 | 
					 | 
					 | 
					        TOCFuture *future = [OWSContactsManager asyncGetAddressBook];
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        [future thenDo:^(id addressBook) {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            // Success.
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            @synchronized(self)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                OWSAssert(self.isContactsUpdateInFlight);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                OWSAssert(!self.addressBookReference);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                self.addressBookReference = addressBook;
 | 
					 | 
					 | 
					 | 
					                self.addressBookReference = addressBook;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                self.isContactsUpdateInFlight = NO;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            }
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            ABAddressBookRef cfAddressBook = (__bridge ABAddressBookRef)addressBook;
 | 
					 | 
					 | 
					 | 
					            ABAddressBookRef cfAddressBook = (__bridge ABAddressBookRef)addressBook;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            ABAddressBookRegisterExternalChangeCallback(cfAddressBook, onAddressBookChanged, (__bridge void *)self);
 | 
					 | 
					 | 
					 | 
					            ABAddressBookRegisterExternalChangeCallback(cfAddressBook, onAddressBookChanged, (__bridge void *)self);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 | 
					 | 
					 | 
					 | 
					            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                [self handleAddressBookChanged];
 | 
					 | 
					 | 
					 | 
					                [self handleAddressBookChanged];
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            });
 | 
					 | 
					 | 
					 | 
					            });
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        }];
 | 
					 | 
					 | 
					 | 
					        }];
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        [future catchDo:^(id failure) {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            // Failure.
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            @synchronized(self)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                OWSAssert(self.isContactsUpdateInFlight);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                OWSAssert(!self.addressBookReference);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                self.isContactsUpdateInFlight = NO;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            }
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        }];
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    });
 | 
					 | 
					 | 
					 | 
					    });
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					- (void)intersectContacts {
 | 
					 | 
					 | 
					 | 
					- (void)intersectContacts {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    [[ContactsUpdater sharedUpdater] updateSignalContactIntersectionWithABContacts:self.allContacts
 | 
					 | 
					 | 
					 | 
					    [self intersectContactsWithRetryDelay:60];
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                                                           success:^{
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					- (void)intersectContactsWithRetryDelay:(CGFloat)retryDelaySeconds
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    void (^success)() = ^{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        DDLogInfo(@"%@ Successfully intersected contacts.", self.tag);
 | 
					 | 
					 | 
					 | 
					        DDLogInfo(@"%@ Successfully intersected contacts.", self.tag);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        [self fireSignalRecipientsDidChange];
 | 
					 | 
					 | 
					 | 
					        [self fireSignalRecipientsDidChange];
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                                                           }
 | 
					 | 
					 | 
					 | 
					    };
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                                                           failure:^(NSError *error) {
 | 
					 | 
					 | 
					 | 
					    void (^failure)(NSError *error) = ^(NSError *error) {
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        DDLogWarn(@"%@ Failed to intersect contacts with error: %@. Rescheduling", self.tag, error);
 | 
					 | 
					 | 
					 | 
					        DDLogWarn(@"%@ Failed to intersect contacts with error: %@. Rescheduling", self.tag, error);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                                                               [NSTimer scheduledTimerWithTimeInterval:60
 | 
					 | 
					 | 
					 | 
					        // Retry with exponential backoff.
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                                                                                                target:self
 | 
					 | 
					 | 
					 | 
					        //
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                                                                                              selector:@selector(intersectContacts)
 | 
					 | 
					 | 
					 | 
					        // TODO: Abort if another contact
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                                                                                              userInfo:nil
 | 
					 | 
					 | 
					 | 
					        // intersection succeeds in the meantime.
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                                                                                               repeats:NO];
 | 
					 | 
					 | 
					 | 
					        dispatch_after(
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                                                           }];
 | 
					 | 
					 | 
					 | 
					            dispatch_time(DISPATCH_TIME_NOW, (int64_t)(retryDelaySeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                [self intersectContactsWithRetryDelay:retryDelaySeconds * 2.f];
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            });
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    };
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    [[ContactsUpdater sharedUpdater] updateSignalContactIntersectionWithABContacts:self.allContacts
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                                                                           success:success
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                                                                           failure:failure];
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					- (void)fireSignalRecipientsDidChange
 | 
					 | 
					 | 
					 | 
					- (void)fireSignalRecipientsDidChange
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
					 | 
					
 
 |