1 #import <TargetConditionals.h>
2 #import <Foundation/Foundation.h>
3 #import <Security/SecXPCHelper.h>
6 #import <xpc/private.h>
7 #endif /* TARGET_OS_WATCH */
9 #import "OTPairingService.h"
10 #import "OTPairingConstants.h"
13 static void create_xpc_listener(xpc_handler_t handler);
14 static void handle_pairing_result(bool success, NSError *error);
15 static void pairing_retry_register(bool checkin);
16 static void pairing_retry_unregister(void);
17 static void ids_retry_init(void);
18 static void ids_retry_enable(bool);
19 #endif /* TARGET_OS_WATCH */
24 static OTPairingService *service;
27 service = [OTPairingService sharedService];
31 /* Check in; handle a possibly-pending retry. */
32 pairing_retry_register(true);
36 create_xpc_listener(^(xpc_object_t message) {
39 reply = xpc_dictionary_create_reply(message);
41 /* Received an explicit pairing request; remove retry activity if one exists. */
42 pairing_retry_unregister();
44 [service initiatePairingWithCompletion:^(bool success, NSError *error) {
45 xpc_connection_t connection;
48 os_log(OS_LOG_DEFAULT, "xpc-initiated pairing succeeded");
50 os_log(OS_LOG_DEFAULT, "xpc-initiated pairing failed: %@", error);
53 xpc_dictionary_set_bool(reply, OTPairingXPCKeySuccess, success);
55 NSData *errdata = [SecXPCHelper encodedDataFromError:error];
56 xpc_dictionary_set_data(reply, OTPairingXPCKeyError, errdata.bytes, errdata.length);
58 connection = xpc_dictionary_get_remote_connection(reply);
59 xpc_connection_send_message(connection, reply);
61 handle_pairing_result(success, error);
64 #endif /* TARGET_OS_WATCH */
71 create_xpc_listener(xpc_handler_t handler)
73 static xpc_connection_t listener;
75 listener = xpc_connection_create_mach_service(OTPairingMachServiceName, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
76 xpc_connection_set_event_handler(listener, ^(xpc_object_t peer) {
77 if (xpc_get_type(peer) != XPC_TYPE_CONNECTION) {
81 // TODO: entitlement check
83 xpc_connection_set_event_handler(peer, ^(xpc_object_t message) {
84 if (xpc_get_type(message) != XPC_TYPE_DICTIONARY) {
88 char *desc = xpc_copy_description(message);
89 os_log(OS_LOG_DEFAULT, "received xpc message: %s", desc);
96 xpc_connection_activate(peer);
98 xpc_connection_activate(listener);
102 handle_pairing_result(bool success, NSError *error)
104 bool actual_success = false;
105 bool standard_retry = false;
106 bool ids_retry = false;
109 actual_success = true;
111 if ([error.domain isEqualToString:OTPairingErrorDomain]) {
112 switch (error.code) {
113 /* AlreadyIn: Treat like success; unregister all retries. */
114 case OTPairingErrorTypeAlreadyIn:
115 actual_success = true;
117 /* Busy: In progress. Do nothing, leave any configured retries. */
118 case OTPairingErrorTypeBusy:
120 /* IDS error. Set up IDS retry _and_ standard retry. */
121 case OTPairingErrorTypeIDS:
122 standard_retry = true;
125 /* Other error, standard retry. */
127 standard_retry = true;
131 standard_retry = true;
135 if (actual_success) {
136 pairing_retry_unregister();
137 ids_retry_enable(false);
139 if (standard_retry) {
140 pairing_retry_register(false);
143 ids_retry_enable(true);
149 pairing_retry_register(bool checkin)
151 xpc_object_t criteria;
154 criteria = XPC_ACTIVITY_CHECK_IN;
156 os_log(OS_LOG_DEFAULT, "scheduling pairing retry");
158 pairing_retry_unregister();
160 criteria = xpc_dictionary_create(NULL, NULL, 0);
161 xpc_dictionary_set_string(criteria, XPC_ACTIVITY_PRIORITY, XPC_ACTIVITY_PRIORITY_MAINTENANCE);
162 xpc_dictionary_set_int64(criteria, XPC_ACTIVITY_INTERVAL, OTPairingXPCActivityInterval);
163 xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_REPEATING, true);
164 xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_ALLOW_BATTERY, true);
165 xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_REQUIRES_CLASS_A, true);
166 xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_COMMUNICATES_WITH_PAIRED_DEVICE, true);
169 xpc_activity_register(OTPairingXPCActivityIdentifier, criteria, ^(xpc_activity_t activity) {
170 xpc_activity_state_t state = xpc_activity_get_state(activity);
171 if (state == XPC_ACTIVITY_STATE_RUN) {
172 os_log(OS_LOG_DEFAULT, "triggered pairing attempt via XPC Activity");
173 OTPairingService *service = [OTPairingService sharedService];
174 [service initiatePairingWithCompletion:^(bool success, NSError *error) {
176 os_log(OS_LOG_DEFAULT, "Pairing retry succeeded");
177 pairing_retry_unregister();
179 os_log(OS_LOG_DEFAULT, "Pairing retry failed: %@", error);
180 // Activity repeats...
188 pairing_retry_unregister(void)
190 xpc_activity_unregister(OTPairingXPCActivityIdentifier);
196 xpc_set_event_stream_handler("com.apple.notifyd.matching", dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(xpc_object_t event) {
200 name = xpc_dictionary_get_string(event, XPC_EVENT_KEY_NAME);
201 if (strcmp(name, OTPairingXPCEventIDSDeviceState) != 0) {
205 state = xpc_dictionary_get_uint64(event, "_State");
206 if ((state & kIDSDeviceStatePropertiesIsNearby) && (state & kIDSDeviceStatePropertiesIsConnected)) {
207 OTPairingService *service = [OTPairingService sharedService];
208 os_log(OS_LOG_DEFAULT, "IDS paired device is connected, retrying");
209 [service initiatePairingWithCompletion:^(bool success, NSError *error) {
211 os_log(OS_LOG_DEFAULT, "IDS notification retry succeeded");
213 os_log(OS_LOG_DEFAULT, "IDS notification retry failed: %@", error);
215 handle_pairing_result(success, error);
222 ids_retry_enable(bool enable)
224 const char *notification;
229 notification = [OTPairingService sharedService].pairedDeviceNotificationName.UTF8String;
230 if (notification != NULL) {
231 dict = xpc_dictionary_create(NULL, NULL, 0);
232 xpc_dictionary_set_string(dict, "Notification", notification);
238 xpc_set_event("com.apple.notifyd.matching", OTPairingXPCEventIDSDeviceState, dict);
241 #endif /* TARGET_OS_WATCH */