likes
comments
collection
share

揭开神秘的面纱:了解tidevice源码中usbmux的工作原理

作者站长头像
站长
· 阅读数 6

前言

作为一名测试工程师,工作中免不了会接触移动端测试工作,对于Android而言,可以使用adbAndroid设备进行通信,从而实现快捷安装、卸载、抓取日志、获取性能数据等功能,但对于Ios来说好像会变得困难一些,幸好有大佬已经开源出相关工具tidevice,该工具用于与iOS设备进行通信,还不了解的同学可以安装使用了解。当然用法并不是我们研究的重点,使用方法在作者提供的文档中已经详细介绍,我们主要研究学习作者的源码,到底是如何实现这样一款强大的工具呢?

usbmuxd

在研究源码之前,我们先了解一下,usbmuxd是啥呢?这样有助于我们更好的查看源码。

usbmuxd的作用

usbmuxd是一个开源的守护进程,用于在iOS设备和计算机之间建立通信通道。它可以让计算机通过USB连接与iOS设备进行通信,以便进行诸如文件传输、应用程序安装和调试等操作。

具体来说,usbmuxd的主要作用包括以下几个方面:

  • 监听来自iOS设备的USB连接请求。
  • 为每个连接到计算机的iOS设备创建一个TCP端口。
  • 将设备上的服务映射到TCP端口上。
  • 允许计算机上运行的应用程序通过TCP/IP协议与设备进行通信,而无需直接使用USB。

usbmuxd的工作原理

usbmuxd的工作原理可以简单概括为以下几个步骤:

  • usbmuxd监听来自iOS设备的USB连接请求。
  • 当设备连接到计算机时,usbmuxd为该设备创建一个TCP端口。
  • usbmuxd将设备上的服务映射到该TCP端口上。
  • 应用程序通过TCP/IP协议与设备进行通信,而无需直接使用USB。

具体来说,当iOS设备连接到计算机时,它会发送一个USB连接请求。usbmuxd会监听这个请求,并为该设备创建一个TCP端口。然后,usbmuxd会将设备上的服务映射到该TCP端口上,例如文件传输服务、调试服务等。最后,应用程序可以通过TCP/IP协议与设备进行通信,例如使用SSH协议进行远程登录、使用iFuse工具进行文件系统挂载等。

usbmuxd的使用方法

usbmuxd是一个跨平台的工具,可以在Linux、macOS和Windows等操作系统上运行。它是开源的,并且可以在GitHub上找到其源代码。它还提供了一些命令行工具,例如iproxy和ifuse,可以帮助开发人员更方便地与iOS设备进行交互。

使用usbmuxd进行iOS开发需要以下几个步骤:

  • 安装usbmuxd工具和相关驱动程序。
  • 连接iOS设备到计算机上。
  • 启动usbmuxd守护进程。
  • 使用相关命令行工具进行文件传输、调试等操作。

一句话

简单点说,其实usbmuxd就是一个服务。而tidevice本质上应该是usbmuxd服务的client

案例解读

tidevice是如何实现这个client

tidevice/_usbmux.py

class Usbmux:
    def __init__(self, address: Optional[Union[str, tuple]] = None):
        pass

    @property
    def address(self) -> str:
        pass
        
    def _next_tag(self) -> int:
        pass

    def create_connection(self) -> PlistSocketProxy:
        pass

    def send_recv(self, payload: dict, timeout: float = None) -> dict:
        pass

    def device_list(self) -> typing.List[DeviceInfo]:
        pass

    def device_udid_list(self) -> typing.List[str]:
        pass

    def _check(self, data: dict):
        pass

    def read_system_BUID(self) -> str:
        pass
      
    def _gen_host_id(self):
        pass
      
    def watch_device(self) -> typing.Iterator[dict]:
        pass

    def connect_device_port(self, devid: int, port: int) -> PlistSocketProxy:
        pass

我们讲解一下这个类的作用,方法的具体实现大家可以去翻源码,文章就不贴了显得都是代码不易阅读:

这是一个名为Usbmux的类,它用于管理与USB设备进行通信的功能。下面是每个方法的作用:

  1. __init__(self, address: Optional[Union[str, tuple]] = None): 这个方法是类的构造函数,用于初始化Usbmux对象。它接受一个可选的address参数,用于指定usbmuxd服务的地址。如果未指定address,则根据操作系统类型自动设置默认地址。
  2. address(self) -> str: 这是一个属性方法,用于获取usbmuxd服务的地址。
  3. _next_tag(self) -> int: 这是一个私有方法,用于生成下一个标签(tag)值。
  4. create_connection(self) -> PlistSocketProxy: 这个方法用于创建与usbmuxd服务的连接,并返回一个PlistSocketProxy对象,该对象用于发送和接收数据。
  5. send_recv(self, payload: dict, timeout: float = None) -> dict: 这个方法用于向usbmuxd服务发送请求,并接收响应数据。它接受一个payload参数,其中包含要发送的数据,还可以指定超时时间。它返回一个包含响应数据的字典。
  6. device_list(self) -> typing.List[DeviceInfo]: 这个方法用于获取连接到计算机的所有USB设备的信息。它向usbmuxd服务发送一个请求来获取设备列表,并将响应数据解析为DeviceInfo对象列表。最后返回设备列表。
  7. device_udid_list(self) -> typing.List[str]: 这个方法返回连接到计算机的所有USB设备的唯一设备标识符(UDID)列表。
  8. _check(self, data: dict): 这是一个私有方法,用于检查usbmuxd服务返回的数据是否包含错误信息。如果出现错误,它会引发MuxReplyError异常。
  9. read_system_BUID(self) -> str: 这个方法用于读取系统的BUID(Baseband Unique ID),它向usbmuxd服务发送一个请求,并返回BUID值。
  10. _gen_host_id(self): 这是一个私有方法,用于生成主机的唯一标识符。
  11. watch_device(self) -> typing.Iterator[dict]: 这个方法返回一个迭代器,用于监听设备连接和断开的事件。它使用PlistSocketProxy对象与usbmuxd服务建立连接,发送请求来监听设备事件,并以字典形式返回事件数据。
  12. connect_device_port(self, devid: int, port: int) -> PlistSocketProxy: 这个方法用于连接到指定USB设备的指定端口。它向usbmuxd服务发送一个连接请求,并返回一个PlistSocketProxy对象,该对象用于与设备进行通信。

方法测试

1、我们调用device_list来获取设备信息,看看调用结果

from tidevice._usbmux import Usbmux

usbmux = Usbmux()
devices = usbmux.device_list()
print(devices)

打印devices,输出结果如下:

[DeviceInfo(udid='00008020-000B65101E06002E', device_id=3900, conn_type=<ConnectionType.USB: 'usb'>)]

2、我们调用device_udid_list来获取设备标识符,看看调用结果

uids = usbmux.device_udid_list()
print(uids)

打印uids,输出结果如下:

['00008020-000B65101E06002E']

最后

Usbmux类提供了与usbmuxd服务交互的各种方法,包括获取设备列表、连接设备端口、监听设备事件等功能。它简化了与USB设备通信的复杂性,并提供了高层次的接口供开发人员使用。当然这里看了一小块,源码中涉及到的PlistSocketPlistSocketProxy我们之后研究的再介绍。