纪宝山 bio photo

纪宝山

背景

汽车之家移动业务发展越来越快,研发团队规模越来越大,在研发、测试阶段对测试设备的资源和成本需求也越来越强烈,测试过程中都很难做到多机型。怎么样合理、高效利用这些测试手机资源,是摆在我们面前的一道难题。

现状

目前的解决方案

  1. 部门内部采购热门机型
  2. 部门之间互相借用设备
  3. 冷门机型、操作系统测试暂不覆盖
  4. 云真机租用设备进行

这些方案在成本、测试周期及效率、用户体验上都有一定的弊端

目标和方案

为了解决这些问题,我们调研了业内的解决方案,在Android端有开源的OpenSTF、百度的MTC、Testin的云真机等等,但是在iOS端,由于iOS平台相对封闭,目前只有某些商业平台的云真机服务且收费很高。 基于以上问题汽车之家测试团队开始自研iOS真机平台。 我们的目标是通过提供稳定的7*24 iOS云真机服务来提升设备的利用率。

用户通过浏览器实现远程iOS设备的查看和操作,示意图如下:

image

参考Android相对开源且成熟的云真机平台实现,移植到iOS端,我们面临几个主要问题:

  1. 如何实现远程真机画面实时地在用户端流畅的显示。(要求帧率较高)
  2. 如何将用户端的操作实时作用于远程真机。(要求延迟较低)
  3. 安装APP程序包和系统日志获取

为了解决这几个问题,我们调研了多个目前应用较多的技术方案,由于我们的目的是模拟用户真实的使用环境,防止测试环境失真,因此不考虑越狱的解决方案。

将iOS设备画面传递给前端用户的远程实时画面回传,主要方法有两种:

  1. iOS设备截屏传输 iOS设备截屏使用开源的libimobiledevice工具库,它实现了iOS设备的信息获取(ideviceinfo)、日志获取(idevicesyslog)、屏幕截图(idevicescreenshot)、安装APP包(ideviceinstaller)等功能,缺点是截图I/O效率不高,最高只能实现每秒2~5帧的屏幕截图。

  2. 获取iOS视频流传输 Mac系统中自带的QuickTime-Player可以对通过USB连接的iOS设备进行流畅的屏幕录制,其原理是使用苹果的AVFoundation和CoreMediaIO库实现iOS设备视频流获取,但是QuickTime-Player是一个封闭的桌面软件无法被集成。 OpenSTF的开源项目iOS-minicap利用AVFoundation和CoreMediaIO库实现视频流获取,使用turbojpeg库实现图像的二进制转换,再通过socket将图像数据流转发到相应端口,此方法传输的速度很快且延迟也很低,可以实现每秒20帧(20FPS)以上流畅的画面传输。

满足每秒20帧以上的画面人眼即视为流畅,考虑使用体验我们选择了视频流传输获取的解决方案iOS-minicap。

将前端对手机进行的点击、拖拽、输入文字等指令传递给远程iOS设备并实现相应操作的远程实时操控,其事件驱动本质与iOS自动化驱动事件类似,我们调研了比较常用的iOS自动化的驱动,主要有:

  1. 苹果官方提供的XCTest和XCUITest
  2. Facebook的开源项目WebDriverAgent(以下简称WDA)
  3. 开源自动化测试工具Appium
  4. 阿里巴巴的开源项目Macaca的XCTestWD

经过调研分析底层实现原理,Appium在iOS自动化测试中使用了WDA作为事件驱动,而WDA底层使用的是苹果的XCTest以及XCUITest,Macaca的XCTestWD的实现原理与WDA类似。远程真机不需要集成自动化测试,所以我们选择WDA作为远程操控事件驱动。

WDA实现了一个后台常驻的HTTP Server,通过xcode的test方式启动WDARunner,接收包含操作指令的HTTP请求,调用XCUITest相应方法操作手机(本质是进行iOS的UI测试),以此实现真机的远程操控。

但在实际应用中,我们的网络无法直接访问iOS设备端建立的HTTP Server,要解决这个问题,需要实现从设备所连接的Mac主机与iOS设备的数据转发,解决方案有两种:

  1. 可以集成在APP中实现iOS与Mac连接的开源方案peertalk
  2. libimobiledevice的开源工具iproxy。

两种方式都是基于苹果usbmuxd(Mac与iOS设备连接的USB驱动)服务的方案,将USB通信抽象为TCP通信。 peertalk是最早实现iOS与Mac连接的开源方案,但是早已停止更新,部署复杂(iOS APP与Mac两端都需要部署),且需要修改大量的源码,集成难度较高;而iproxy是libimobiledevice实现了苹果usbmuxd服务的开源工具,仅需要部署在Mac端,使用方便,所以我们选择iproxy实现协议转发。

安装APP程序包和系统日志获取目前只有libimobiledevice的 ideviceinstaller(安装APP)和idevicesyslog(系统日志获取)。 基于以上,我们设计了iOS远程真机实现方案,示意图如下:

image

根据业务场景需要,多个用户同时访问多台设备,并且需要将被占用的设备设置不可用,因此我们设计了可以实现业务需求的项目的整体架构。 项目整体架构如下图:

image

系统总体属于B/S结构,分为几个模块。 Web前端:用户使用的浏览器,实现查询iOS设备状态、发送连接请求,根据请求返回的Mac设备信息与相应的Mac设备进行Websocket直连。

Django后台:用户身份验证,查询Redis中的iOS设备信息及占用状态,展示给前端,接收前端的iOS连接请求转发至统一调度平台,将统一调度平台返回的Mac设备信息发送回Web前端。

统一调度平台:接收Django后台的连接请求,通过查询Redis中Mac设备信息,连接相应Mac设备中的iOS设备调度程序,发送连接请求,并将iOS设备调度程序返回的结果发送回Django后台。

iOS设备调度:每一个连接iOS设备的Mac主机都运行一个iOS设备调度程序,它收集Mac设备上连接的iOS设备信息并上传至Redis,通过RPC与统一调度平台连接接收请求,在用户使用时,与Web前端进行Websocket直连。

遇到的问题

我们按照这个结构进行项目搭建并进行了测试,但发现了一些问题:

  • WDA点击操作无法进行敏感权限操作(如删除APP、授权信任证书等)
  • WDA实现的滑动缓慢且延迟较高
  • iOS-minicap存在帧率不稳定、画面卡死现象
  • WDA与iOS-minicap启动冲突导致启动失败

针对前两个问题,我们对WDA项目代码进行了优化,使用了苹果私有API,私有API的头文件可以使用RuntimeBrowser等iOS逆向工具导出。优化后实现了:

  • 提升权限的点击操作
  • 毫秒级的点击响应
  • 200ms以内延迟的滑动响应
  • 稳定20+FPS的画面输出
  1. 重写了点击(Tap)方法 image 点击方法使用了XCEventGenerator私有API,输入点坐标即可实现点击操作,长按(TapAndHold)只需在此基础上增加持续时间(duration)参数即可,解决了原有方法中敏感操作的权限问题,也减少了延迟,实现毫秒级的响应。

  2. 重写了滑动(Drag)方法 image 滑动方法同样使用了XCEventGenerator私有API,输入起点、终点坐标与持续时间即可实现滑动操作。此方法可以将滑动延迟控制在200ms以内,大大提升了滑动操作的流畅度。

  3. iOS-minicap的实现方法中并没有控制获取视频流转图片发送至iOS-minicap端口的帧率限制,导致Mac设备实现的帧率不稳定,在通过Websocket转发时会产生时快时慢的卡顿现象以及阻塞卡死现象,我们在代码中加入了一个40毫秒的timer,保证帧率不高于25帧,实现了高帧率(20-25FPS)的稳定画面输出。

  4. WDA的启动与iOS-minicap启动都会占用xcodebuild资源,同时启动会引起冲突并且导致WDA的启动失败,我们将WDA的启动延迟,在iOS设备调度程序中加入一个控制WDA启动的全局变量,在iOS-minicap启动完成并发送第一帧图片帧之后再启动WDA,解决冲突问题。 与此同时,我们还实现了日志流输出、字符输入/清除等功能。

实现的效果如下图:

image

image

后续规划:

我们计划继续扩展产品功能,比如优化log日志展示,IDE远程调试(正在开发中),iOS可视化自动化测试、设备集群、多屏操作、提供服务接口等,我们也会不断的迭代版本、打磨产品,提供更好的使用体验。 最后欢迎有兴趣的同学与我联系,共同探讨共同进步,也欢迎同学们参与到我们的项目。 更多精彩技术文章,欢迎大家访问汽车之家系统平台团队博客http://autohomeops.corpautohome.com

image