OTPairingClient.m   [plain text]


#import <TargetConditionals.h>

#if TARGET_OS_WATCH

#import <Foundation/Foundation.h>
#import <xpc/xpc.h>
#import <os/log.h>
#import <Security/SecXPCHelper.h>

#import "keychain/categories/NSError+UsefulConstructors.h"

#import "OTPairingClient.h"
#import "OTPairingConstants.h"

void
OTPairingInitiateWithCompletion(dispatch_queue_t queue, void (^completion)(bool success, NSError *))
{
    xpc_connection_t connection;
    xpc_object_t message;

    connection = xpc_connection_create_mach_service(OTPairingMachServiceName, NULL, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
    xpc_connection_set_event_handler(connection, ^(__unused xpc_object_t event) {
    });
    xpc_connection_activate(connection);

    message = xpc_dictionary_create(NULL, NULL, 0);
    xpc_dictionary_set_uint64(message, OTPairingXPCKeyOperation, OTPairingOperationInitiate);

    xpc_connection_send_message_with_reply(connection, message, queue, ^(xpc_object_t reply) {
        if (xpc_get_type(reply) == XPC_TYPE_DICTIONARY) {
            bool success = xpc_dictionary_get_bool(reply, OTPairingXPCKeySuccess);
            size_t errlen = 0;
            const void *errptr = xpc_dictionary_get_data(reply, OTPairingXPCKeyError, &errlen);
            NSData *errordata;
            NSError *nserr = nil;

            if (errptr != NULL) {
                errordata = [NSData dataWithBytesNoCopy:(void *)errptr length:errlen freeWhenDone:NO];
                nserr = [SecXPCHelper errorFromEncodedData:errordata];
            }

            completion(success, nserr);
        } else if (reply == XPC_ERROR_CONNECTION_INVALID) {
            // This error is expected; otpaird is a NanoLaunchDaemon, only loaded when companion is paired.
            os_log(OS_LOG_DEFAULT, "otpaird connection invalid (daemon unloaded?)");
            completion(true, nil);
        } else {
            char *desc = NULL;
            NSDictionary *userInfo = nil;

            desc = xpc_copy_description(reply);
            userInfo = @{ NSLocalizedDescriptionKey : [NSString stringWithUTF8String:desc] };
            free(desc);

            completion(false, [NSError errorWithDomain:OTPairingErrorDomain code:OTPairingErrorTypeXPC userInfo:userInfo]);
        }
    });
}

#endif /* TARGET_OS_WATCH */