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
;
88 #pragma mark -- Tracker
90 @interface CKKSAccountStateTracker
: NSObject
<CKKSCloudKitAccountStateTrackingProvider
,
91 CKKSOctagonStatusMemoizer
>
92 @property CKKSCondition
* finishedInitialDispatches
;
94 // If you use these, please be aware they could change out from under you at any time
95 @
property (nullable
) CKAccountInfo
* currentCKAccountInfo
;
96 @property CKKSCondition
* ckAccountInfoInitialized
;
99 // Fetched and memoized from CloudKit; we can't afford deadlocks with their callbacks
100 @
property (nullable
, copy
) NSString
* ckdeviceID
;
101 @
property (nullable
) NSError
* ckdeviceIDError
;
102 @property CKKSCondition
* ckdeviceIDInitialized
;
104 // Fetched and memoized from SOS. Not otherwise used.
105 @
property (nullable
) SOSAccountStatus
* currentCircleStatus
;
106 @
property (nullable
) NSString
* accountCirclePeerID
;
107 @
property (nullable
) NSError
* accountCirclePeerIDError
;
108 @property CKKSCondition
* accountCirclePeerIDInitialized
;
110 // Filled and memoized for quick reference. Don't use for anything vital.
111 // This will only fetch the status for the default context.
112 @
property (readonly
, nullable
) OTCliqueStatusWrapper
* octagonStatus
;
113 @
property (readonly
, nullable
) NSString
* octagonPeerID
;
114 @
property (readonly
) CKKSCondition
* octagonInformationInitialized
;
116 // Filled by Octagon, as it's fairly hard to compute.
117 @
property (readonly
) CKKSAccountStatus hsa2iCloudAccountStatus
;
118 @
property (readonly
) CKKSCondition
* hsa2iCloudAccountInitialized
;
120 - (instancetype
)init
:(CKContainer
*)container nsnotificationCenterClass
:(Class
<CKKSNSNotificationCenter
>)nsnotificationCenterClass
;
122 - (dispatch_semaphore_t
)registerForNotificationsOfCloudKitAccountStatusChange
:(id
<CKKSCloudKitAccountStateListener
>)listener
;
124 // Call this to refetch the Octagon status
125 - (void)triggerOctagonStatusFetch
;
127 // Methods useful for testing:
128 - (void)performInitialDispatches
;
130 // Call this to simulate a notification (and pause the calling thread until all notifications are delivered)
131 - (void)notifyCKAccountStatusChangeAndWaitForSignal
;
132 - (void)notifyCircleStatusChangeAndWaitForSignal
;
134 - (dispatch_group_t _Nullable
)checkForAllDeliveries
;
136 - (void)setHSA2iCloudAccountStatus
:(CKKSAccountStatus
)status
;
138 + (SOSAccountStatus
*)getCircleStatus
;
139 + (void)fetchCirclePeerID
:(void (^)(NSString
* _Nullable peerID
, NSError
* _Nullable error
))callback
;
140 + (NSString
*)stringFromAccountStatus
:(CKKSAccountStatus
)status
;
144 NS_ASSUME_NONNULL_END