searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

[usbmux]macOS是如何与iDevice通信的

2023-07-18 09:57:31
192
0

usbmux是一套基于socket的通信协议,在macOS主要实现为usbmuxd,在macOS启动时随之启动,其主要涉及到以下文件:

1. /System/Library/PrivateFrameworks/MobileDevice.framework/Versions/A/Resources/usbmuxd

2. /var/run/usbmuxd

第一个为可执行文件,也就是守护进程usbmuxd的启动文件;usbmuxd进程启动之后会创建socket(Unix Domain Socket),对应的文件为第二个文件,macOS的所有App都是通过这个socket和usbmuxd进程通信。

设备间通信过程

按照官方说法,usbmuxd提供了一种TCP-Like的方式允许macOS App和iOS设备通信,比如上图,Xcode和iOS上的lockdown服务之间通过usbmuxd和USB Service建立了类似TCP链接的通信通道,这种通信方式类似C/S架构,其中Xcode是客户端(Client),lockdown是服务端(Server)。

lockdown服务

lockdown在iOS系统由launchd进程启动,它主要的角色是充当网关,协调macOS和iOS其他服务的通信。

lockdown启动之后会建立一个端口为62078的socket listen,监听相关连接请求。

比如Xcode要进行设备调试,需要先和lockdown服务通信,发起调试请求,lockdown再启动调试服务(debugserver),然后Xcode才能和debugserver建立连接。

 

usbmuxd服务

usbmuxd运行在macOS系统上,通过USB协议和iOS设备通信。

macOS App通过Unix Domain Socket和usbmuxd通信。

int connect_to_usbmuxd() {
    // Create Unix domain socket
    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
    // prevent SIGPIPE
    int on = 1;
    setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));

    // Connect socket
    struct sockaddr_un addr;
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, "/var/run/usbmuxd");
    socklen_t socklen = sizeof(addr);

    if (connect(fd, (struct sockaddr *)&addr, socklen) == -1) {
        printf("Connect failure, fd is: %d.\n", fd);
    } else {
        printf("Connect successifully, fd is: %d.\n", fd);
    }

    return fd;
}

 

连接过程

usbmux协议使用xml格式作为消息体,主要定义了下面这些消息来协商连接过程:

// usbmuxd的消息格式使用xml也就是plist
void send_packet(NSDictionary *packetDict, int tag, dispatch_io_t channel) {
    NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:packetDict format:NSPropertyListXMLFormat_v1_0 options:0 error:NULL];
    
    int protocol = USBMuxPacketProtocolPlist;
    int type = USBMuxPacketTypePlistPayload;
    
    usbmux_packet_t *upacket = usbmux_packet_create(
                                                    protocol,
                                                    type,
                                                    tag,
                                                    plistData ? plistData.bytes : nil,
                                                    (uint32_t)(plistData.length)
                                                    );
    
    dispatch_data_t data = dispatch_data_create((const void*)upacket, upacket->size, usbmuxd_io_queue, ^{
        usbmux_packet_free(upacket);
    });
    
    dispatch_io_write(channel, 0, data, usbmuxd_io_queue, ^(bool done, dispatch_data_t data, int _errno) {
        NSLog(@"dispatch_io_write: done=%d data=%p error=%d", done, data, _errno);
        if (!done) { return; }
    });
}

1. Listen

Xcode连接到usbmuxd进程之后,需要发送Listen消息来告诉usbmuxd开始监听iOS设备的连接通知,以便在iOS设备通过USB接入之后能够获取到设备信息,从而决定是否与设备建立连接。

void send_listen_packet() {
    connect_channel = connect_to_usbmuxd_channel();

    NSDictionary *packet = @{
                             @"ClientVersionString": @"1",
                             @"MessageType": @"Listen",
                             @"ProgName": @"Peertalk Example"
                             };
    NSLog(@"send listen packet: %@", packet);
    send_packet(packet, 0, listen_channel);
}

当有iOS设备连接,就会收到来自usbmuxd的设备消息“DeviceList”
主要的格式如下:

[{
    MessageType =  Attached",
    DeviceID = 19,
    Properties = {
        ConnectionSpeed = 480000000,
        DeviceID = 19,
        LocationID = 338821120,
        ProductID = 4776,
        SerialNumber = ...,
        USBSerialNumber = ...
    }
}]

2. Connect

上一步我们已经拿到了设备相关信息,接下来就可以连接设备,通过发送Connect消息来建立设备连接。

void send_connect_usb_packet() {
    connect_channel = connect_to_usbmuxd_channel();
    
    port = ((port<<8) & 0xFF00) | (port>>8);
    NSDictionary *packet = @{
                             @"ClientVersionString" : @"1",
                             @"DeviceID" : deviceID,
                             @"MessageType" : @"Connect",
                             @"PortNumber" : [NSNumber numberWithInt:port],
                             @"ProgName" : @"Peertalk Example"
                             };
    
    NSLog(@"send connect to usb packet: %@", packet);
    send_packet(packet, 1, connect_channel);
    read_packet_on_channle(connect_channel);
}

其中PortNumber就是我们想要连接到的服务,比如lockdown服务的端口就是62078。

如果连接成功,就能收到结果消息:

{
    MessageType = Result;
    Number = 0;
}

至此,Xcode就建立了和iOS设备上的lockdown服务的TCP连接,就可以直接通过这个连接通道发送TCP数据了。

void send_msg(NSString *msg) {
    dispatch_data_t payload = create_payload(msg);
    dispatch_data_t frame = create_frame(101, 0, payload);

    // send through connect channel, not tcp_channel
    dispatch_io_write(connect_channel, 0, frame, usbmuxd_io_queue, ^(bool done, dispatch_data_t  _Nullable data, int error) {
        NSLog(@"error is: %d", error);
    });
}

 

主要的过程总结如下:

1. macOS App通过Unix Domain Socket和usbmuxd进行通信(类似TCP);

2. macOS App向usbmuxd发送Listen命令,监听iOS连接状态;

3. macOS App接收iOS设备信息,发送Connect命令,建立与iOS端相关进程连接(基于端口号);

4. lockdown服务以TCP Server的方式存在;

5. usbmuxd作为代理通过usbmux协议为macOS App和iOS设备进程提供了类TCP的通信服务;

0条评论
0 / 1000
l****n
14文章数
1粉丝数
l****n
14 文章 | 1 粉丝
原创

[usbmux]macOS是如何与iDevice通信的

2023-07-18 09:57:31
192
0

usbmux是一套基于socket的通信协议,在macOS主要实现为usbmuxd,在macOS启动时随之启动,其主要涉及到以下文件:

1. /System/Library/PrivateFrameworks/MobileDevice.framework/Versions/A/Resources/usbmuxd

2. /var/run/usbmuxd

第一个为可执行文件,也就是守护进程usbmuxd的启动文件;usbmuxd进程启动之后会创建socket(Unix Domain Socket),对应的文件为第二个文件,macOS的所有App都是通过这个socket和usbmuxd进程通信。

设备间通信过程

按照官方说法,usbmuxd提供了一种TCP-Like的方式允许macOS App和iOS设备通信,比如上图,Xcode和iOS上的lockdown服务之间通过usbmuxd和USB Service建立了类似TCP链接的通信通道,这种通信方式类似C/S架构,其中Xcode是客户端(Client),lockdown是服务端(Server)。

lockdown服务

lockdown在iOS系统由launchd进程启动,它主要的角色是充当网关,协调macOS和iOS其他服务的通信。

lockdown启动之后会建立一个端口为62078的socket listen,监听相关连接请求。

比如Xcode要进行设备调试,需要先和lockdown服务通信,发起调试请求,lockdown再启动调试服务(debugserver),然后Xcode才能和debugserver建立连接。

 

usbmuxd服务

usbmuxd运行在macOS系统上,通过USB协议和iOS设备通信。

macOS App通过Unix Domain Socket和usbmuxd通信。

int connect_to_usbmuxd() {
    // Create Unix domain socket
    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
    // prevent SIGPIPE
    int on = 1;
    setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));

    // Connect socket
    struct sockaddr_un addr;
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, "/var/run/usbmuxd");
    socklen_t socklen = sizeof(addr);

    if (connect(fd, (struct sockaddr *)&addr, socklen) == -1) {
        printf("Connect failure, fd is: %d.\n", fd);
    } else {
        printf("Connect successifully, fd is: %d.\n", fd);
    }

    return fd;
}

 

连接过程

usbmux协议使用xml格式作为消息体,主要定义了下面这些消息来协商连接过程:

// usbmuxd的消息格式使用xml也就是plist
void send_packet(NSDictionary *packetDict, int tag, dispatch_io_t channel) {
    NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:packetDict format:NSPropertyListXMLFormat_v1_0 options:0 error:NULL];
    
    int protocol = USBMuxPacketProtocolPlist;
    int type = USBMuxPacketTypePlistPayload;
    
    usbmux_packet_t *upacket = usbmux_packet_create(
                                                    protocol,
                                                    type,
                                                    tag,
                                                    plistData ? plistData.bytes : nil,
                                                    (uint32_t)(plistData.length)
                                                    );
    
    dispatch_data_t data = dispatch_data_create((const void*)upacket, upacket->size, usbmuxd_io_queue, ^{
        usbmux_packet_free(upacket);
    });
    
    dispatch_io_write(channel, 0, data, usbmuxd_io_queue, ^(bool done, dispatch_data_t data, int _errno) {
        NSLog(@"dispatch_io_write: done=%d data=%p error=%d", done, data, _errno);
        if (!done) { return; }
    });
}

1. Listen

Xcode连接到usbmuxd进程之后,需要发送Listen消息来告诉usbmuxd开始监听iOS设备的连接通知,以便在iOS设备通过USB接入之后能够获取到设备信息,从而决定是否与设备建立连接。

void send_listen_packet() {
    connect_channel = connect_to_usbmuxd_channel();

    NSDictionary *packet = @{
                             @"ClientVersionString": @"1",
                             @"MessageType": @"Listen",
                             @"ProgName": @"Peertalk Example"
                             };
    NSLog(@"send listen packet: %@", packet);
    send_packet(packet, 0, listen_channel);
}

当有iOS设备连接,就会收到来自usbmuxd的设备消息“DeviceList”
主要的格式如下:

[{
    MessageType =  Attached",
    DeviceID = 19,
    Properties = {
        ConnectionSpeed = 480000000,
        DeviceID = 19,
        LocationID = 338821120,
        ProductID = 4776,
        SerialNumber = ...,
        USBSerialNumber = ...
    }
}]

2. Connect

上一步我们已经拿到了设备相关信息,接下来就可以连接设备,通过发送Connect消息来建立设备连接。

void send_connect_usb_packet() {
    connect_channel = connect_to_usbmuxd_channel();
    
    port = ((port<<8) & 0xFF00) | (port>>8);
    NSDictionary *packet = @{
                             @"ClientVersionString" : @"1",
                             @"DeviceID" : deviceID,
                             @"MessageType" : @"Connect",
                             @"PortNumber" : [NSNumber numberWithInt:port],
                             @"ProgName" : @"Peertalk Example"
                             };
    
    NSLog(@"send connect to usb packet: %@", packet);
    send_packet(packet, 1, connect_channel);
    read_packet_on_channle(connect_channel);
}

其中PortNumber就是我们想要连接到的服务,比如lockdown服务的端口就是62078。

如果连接成功,就能收到结果消息:

{
    MessageType = Result;
    Number = 0;
}

至此,Xcode就建立了和iOS设备上的lockdown服务的TCP连接,就可以直接通过这个连接通道发送TCP数据了。

void send_msg(NSString *msg) {
    dispatch_data_t payload = create_payload(msg);
    dispatch_data_t frame = create_frame(101, 0, payload);

    // send through connect channel, not tcp_channel
    dispatch_io_write(connect_channel, 0, frame, usbmuxd_io_queue, ^(bool done, dispatch_data_t  _Nullable data, int error) {
        NSLog(@"error is: %d", error);
    });
}

 

主要的过程总结如下:

1. macOS App通过Unix Domain Socket和usbmuxd进行通信(类似TCP);

2. macOS App向usbmuxd发送Listen命令,监听iOS连接状态;

3. macOS App接收iOS设备信息,发送Connect命令,建立与iOS端相关进程连接(基于端口号);

4. lockdown服务以TCP Server的方式存在;

5. usbmuxd作为代理通过usbmux协议为macOS App和iOS设备进程提供了类TCP的通信服务;

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0