CKKSCKAccountStateTracker.h   [plain text]


/*
 * Copyright (c) 2017 Apple Inc. All Rights Reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */

#import <Foundation/Foundation.h>

#if OCTAGON
#import <CloudKit/CloudKit.h>
#import <CloudKit/CKContainer_Private.h>
#import "keychain/ckks/CloudKitDependencies.h"
#import "keychain/ckks/CKKSCondition.h"
#include <Security/SecureObjectSync/SOSCloudCircle.h>

/*
 * Implements a 'debouncer' to store the current CK account and circle state, and receive updates to it.
 *
 * Will only be considered "logged in" if we both have a CK account and are 'in circle'.
 *
 * It will notify listeners on account state changes, so multiple repeated account state notifications with the same state are filtered by this class.
 * Listeners can also get the 'current' state, no matter what it is. They will also then be atomically added to the notification queue, and so will
 * always receive the next update, preventing them from getting a stale state and missing an immediate update.
 */

typedef NS_ENUM(NSInteger, CKKSAccountStatus) {
    /* Set at initialization. This means we haven't figured out what the account state is. */
    CKKSAccountStatusUnknown                             = 0,
    /* We have an iCloud account and are in-circle */
    CKKSAccountStatusAvailable                           = 1,
    /* No iCloud account is logged in on this device, or we're out of circle */
    CKKSAccountStatusNoAccount                           = 3,
};

@protocol CKKSAccountStateListener
-(void)ckAccountStatusChange: (CKKSAccountStatus)oldStatus to:(CKKSAccountStatus)currentStatus;
@end

@interface CKKSCKAccountStateTracker : NSObject
@property CKKSCondition* finishedInitialCalls;

// If you use these, please be aware they could change out from under you at any time
@property CKAccountInfo* currentCKAccountInfo;
@property SOSCCStatus currentCircleStatus;

// Fetched and memoized from CloudKit; we can't afford deadlocks with their callbacks
@property (copy) NSString* ckdeviceID;
@property NSError*         ckdeviceIDError;
@property CKKSCondition*   ckdeviceIDInitialized;

// Fetched and memoized from the Account when we're in-circle; our threading model is strange
@property NSString*      accountCirclePeerID;
@property NSError*       accountCirclePeerIDError;
@property CKKSCondition* accountCirclePeerIDInitialized;

-(instancetype)init: (CKContainer*) container nsnotificationCenterClass: (Class<CKKSNSNotificationCenter>) nsnotificationCenterClass;

-(CKKSAccountStatus)currentCKAccountStatusAndNotifyOnChange: (id<CKKSAccountStateListener>) listener;

// Methods useful for testing:

// Call this to simulate a notification (and pause the calling thread until all notifications are delivered)
-(void)notifyCKAccountStatusChangeAndWaitForSignal;
-(void)notifyCircleStatusChangeAndWaitForSignal;

+(SOSCCStatus)getCircleStatus;
+(void)fetchCirclePeerID:(void (^)(NSString* peerID, NSError* error))callback;
+(NSString*)stringFromAccountStatus: (CKKSAccountStatus) status;

@end

#endif // OCTAGON