2 * Copyright (c) 2017 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #import <Foundation/Foundation.h>
27 #import <CloudKit/CKContainer_Private.h>
28 #import <CloudKit/CloudKit.h>
29 #include <Security/SecureObjectSync/SOSCloudCircle.h>
30 #import "keychain/ckks/CKKSCondition.h"
31 #import "keychain/ckks/CloudKitDependencies.h"
33 NS_ASSUME_NONNULL_BEGIN
36 * Implements a 'debouncer' to store the current CK account and circle state, and receive updates to it.
38 * Will only be considered "logged in" if we both have a CK account and are 'in circle'.
40 * It will notify listeners on account state changes, so multiple repeated account state notifications with the same state are filtered by this class.
41 * 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
42 * always receive the next update, preventing them from getting a stale state and missing an immediate update.
45 typedef NS_ENUM(NSInteger
, CKKSAccountStatus
) {
46 /* Set at initialization. This means we haven't figured out what the account state is. */
47 CKKSAccountStatusUnknown
= 0,
48 /* We have an iCloud account and are in-circle */
49 CKKSAccountStatusAvailable
= 1,
50 /* No iCloud account is logged in on this device, or we're out of circle */
51 CKKSAccountStatusNoAccount
= 3,
54 @interface SOSAccountStatus
: NSObject
55 @property SOSCCStatus status
;
56 @
property (nullable
) NSError
* error
;
57 - (instancetype
)init
:(SOSCCStatus
)status error
:error
;
62 typedef NS_ENUM(NSInteger
, CliqueStatus
) {
63 CliqueStatusIn
= 0, /*There is a clique and I am in it*/
64 CliqueStatusNotIn
= 1, /*There is a clique and I am not in it - you should get a voucher to join or tell another peer to trust us*/
65 CliqueStatusPending
= 2, /*For compatibility, keeping the pending state */
66 CliqueStatusAbsent
= 3, /*There is no clique - you can establish one */
67 CliqueStatusNoCloudKitAccount
= 4, /* no cloudkit account present */
68 CliqueStatusError
= -1 /*unable to determine circle status, inspect CFError to find out why */
70 NSString
* OTCliqueStatusToString(CliqueStatus status
);
71 CliqueStatus
OTCliqueStatusFromString(NSString
* str
);
73 @interface OTCliqueStatusWrapper
: NSObject
74 @
property (readonly
) CliqueStatus status
;
75 - (instancetype
)initWithStatus
:(CliqueStatus
)status
;
78 /* End of clique status */
80 @protocol CKKSAccountStateListener
<NSObject
>
81 - (void)ckAccountStatusChange
:(CKKSAccountStatus
)oldStatus to
:(CKKSAccountStatus
)currentStatus
;
84 @interface CKKSCKAccountStateTracker
: NSObject
85 @property CKKSCondition
* finishedInitialDispatches
;
87 // If you use these, please be aware they could change out from under you at any time
88 @
property (nullable
) CKAccountInfo
* currentCKAccountInfo
;
89 @
property (nullable
) SOSAccountStatus
* currentCircleStatus
;
91 @
property (readonly
,atomic
) CKKSAccountStatus currentComputedAccountStatus
;
92 @
property (nullable
,readonly
,atomic
) NSError
* currentAccountError
;
93 @property CKKSCondition
* currentComputedAccountStatusValid
;
95 // Fetched and memoized from CloudKit; we can't afford deadlocks with their callbacks
96 @
property (nullable
, copy
) NSString
* ckdeviceID
;
97 @
property (nullable
) NSError
* ckdeviceIDError
;
98 @property CKKSCondition
* ckdeviceIDInitialized
;
100 // Fetched and memoized from the Account when we're in-circle; our threading model is strange
101 @
property (nullable
) NSString
* accountCirclePeerID
;
102 @
property (nullable
) NSError
* accountCirclePeerIDError
;
103 @property CKKSCondition
* accountCirclePeerIDInitialized
;
105 - (instancetype
)init
:(CKContainer
*)container nsnotificationCenterClass
:(Class
<CKKSNSNotificationCenter
>)nsnotificationCenterClass
;
107 - (dispatch_semaphore_t
)notifyOnAccountStatusChange
:(id
<CKKSAccountStateListener
>)listener
;
109 // Methods useful for testing:
111 // Call this to simulate a notification (and pause the calling thread until all notifications are delivered)
112 - (void)notifyCKAccountStatusChangeAndWaitForSignal
;
113 - (void)notifyCircleStatusChangeAndWaitForSignal
;
115 - (dispatch_group_t _Nullable
)checkForAllDeliveries
;
117 + (SOSAccountStatus
*)getCircleStatus
;
118 + (void)fetchCirclePeerID
:(void (^)(NSString
* _Nullable peerID
, NSError
* _Nullable error
))callback
;
119 + (NSString
*)stringFromAccountStatus
:(CKKSAccountStatus
)status
;
123 NS_ASSUME_NONNULL_END