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"
32 #import "keychain/ot/OTClique.h"
34 NS_ASSUME_NONNULL_BEGIN
37 * Implements a 'debouncer' to store the current CK account and circle state, and receive updates to it.
39 * You can register for CK account changes, SOS account changes, or to be informed only when both are in
42 * It will notify listeners on account state changes, so multiple repeated account state notifications with the same state are filtered by this class.
43 * 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
44 * always receive the next update, preventing them from getting a stale state and missing an immediate update.
47 // This enum represents the combined states of a CK account and the SOS account
48 typedef NS_ENUM(NSInteger
, CKKSAccountStatus
) {
49 /* Set at initialization. This means we haven't figured out what the account state is. */
50 CKKSAccountStatusUnknown
= 0,
51 /* We have an iCloud account and are in-circle */
52 CKKSAccountStatusAvailable
= 1,
53 /* No iCloud account is logged in on this device, or we're out of circle */
54 CKKSAccountStatusNoAccount
= 3,
56 NSString
* CKKSAccountStatusToString(CKKSAccountStatus status
);
58 @interface SOSAccountStatus
: NSObject
59 @property SOSCCStatus status
;
60 @
property (nullable
) NSError
* error
;
61 - (instancetype
)init
:(SOSCCStatus
)status error
:error
;
64 @interface OTCliqueStatusWrapper
: NSObject
65 @
property (readonly
) CliqueStatus status
;
66 - (instancetype
)initWithStatus
:(CliqueStatus
)status
;
69 @protocol CKKSOctagonStatusMemoizer
70 - (void)triggerOctagonStatusFetch
;
72 @
property (readonly
, nullable
) OTCliqueStatusWrapper
* octagonStatus
;
73 @
property (readonly
, nullable
) NSString
* octagonPeerID
;
75 // A little bit of a abstraction violation, but it'll do.
76 - (void)setHSA2iCloudAccountStatus
:(CKKSAccountStatus
)status
;
79 #pragma mark -- Listener Protocols
81 @protocol CKKSCloudKitAccountStateListener
<NSObject
>
82 - (void)cloudkitAccountStateChange
:(CKAccountInfo
* _Nullable
)oldAccountInfo to
:(CKAccountInfo
*)currentAccountInfo
;
84 @protocol CKKSCloudKitAccountStateTrackingProvider
<NSObject
>
85 - (dispatch_semaphore_t
)registerForNotificationsOfCloudKitAccountStatusChange
:(id
<CKKSCloudKitAccountStateListener
>)listener
;
86 - (BOOL
)notifyCKAccountStatusChangeAndWait
:(dispatch_time_t
)timeout
;
89 #pragma mark -- Tracker
91 @interface CKKSAccountStateTracker
: NSObject
<CKKSCloudKitAccountStateTrackingProvider
,
92 CKKSOctagonStatusMemoizer
>
93 @property CKKSCondition
* finishedInitialDispatches
;
95 // If you use these, please be aware they could change out from under you at any time
96 @
property (nullable
) CKAccountInfo
* currentCKAccountInfo
;
97 @property CKKSCondition
* ckAccountInfoInitialized
;
100 // Fetched and memoized from CloudKit; we can't afford deadlocks with their callbacks
101 @
property (nullable
, copy
) NSString
* ckdeviceID
;
102 @
property (nullable
) NSError
* ckdeviceIDError
;
103 @property CKKSCondition
* ckdeviceIDInitialized
;
105 // Fetched and memoized from SOS. Not otherwise used.
106 @
property (nullable
) SOSAccountStatus
* currentCircleStatus
;
107 @
property (nullable
) NSString
* accountCirclePeerID
;
108 @
property (nullable
) NSError
* accountCirclePeerIDError
;
109 @property CKKSCondition
* accountCirclePeerIDInitialized
;
111 // Filled and memoized for quick reference. Don't use for anything vital.
112 // This will only fetch the status for the default context.
113 @
property (readonly
, nullable
) OTCliqueStatusWrapper
* octagonStatus
;
114 @
property (readonly
, nullable
) NSString
* octagonPeerID
;
115 @
property (readonly
) CKKSCondition
* octagonInformationInitialized
;
117 // Filled by Octagon, as it's fairly hard to compute.
118 @
property (readonly
) CKKSAccountStatus hsa2iCloudAccountStatus
;
119 @
property (readonly
) CKKSCondition
* hsa2iCloudAccountInitialized
;
121 - (instancetype
)init
:(CKContainer
*)container nsnotificationCenterClass
:(Class
<CKKSNSNotificationCenter
>)nsnotificationCenterClass
;
123 - (dispatch_semaphore_t
)registerForNotificationsOfCloudKitAccountStatusChange
:(id
<CKKSCloudKitAccountStateListener
>)listener
;
125 // Call this to refetch the Octagon status
126 - (void)triggerOctagonStatusFetch
;
128 // Methods useful for testing:
129 - (void)performInitialDispatches
;
131 // Call this to simulate a notification (and pause the calling thread until all notifications are delivered)
132 - (void)notifyCKAccountStatusChangeAndWaitForSignal
;
133 - (void)notifyCircleStatusChangeAndWaitForSignal
;
135 - (dispatch_group_t _Nullable
)checkForAllDeliveries
;
137 - (void)setHSA2iCloudAccountStatus
:(CKKSAccountStatus
)status
;
139 + (SOSAccountStatus
*)getCircleStatus
;
140 + (void)fetchCirclePeerID
:(void (^)(NSString
* _Nullable peerID
, NSError
* _Nullable error
))callback
;
141 + (NSString
*)stringFromAccountStatus
:(CKKSAccountStatus
)status
;
145 NS_ASSUME_NONNULL_END