相关文章推荐
威武的南瓜  ·  adb logcat -g - CSDN文库·  3 天前    · 
淡定的盒饭  ·  清除adb logcat缓存_adb ...·  3 天前    · 
踏实的茄子  ·  清除adb ...·  4 天前    · 
绅士的炒饭  ·  SQL server ...·  3 月前    · 

记一次 WebUSB 设备连接的奇怪现象

1 年前

首先给大家推荐一个我最近在看的开源项目: GitHub - yume-chan/ya-webadb: ADB in your browser ,算是目前社区我认为最好用的 WebADB 实现了。

也正是因为最新在使用 ya-webadb 在做一个内部项目,发现一个奇怪的现象,就是一开始我在本地调试使用 localhost:3000 在调试,是可以正常连接上设备的,但是当我因为一些原因,我更换了端口为 8000,此时在 await device.connect() 时,既没有报错,也没有返回,于是好奇之下,我开始从源码开始阅读和调试了。

首先,WebADB 是基于 WebUSB 实现的,即通过 WebUSB 读取数据,然后又按照 ADB 官方的数据协议文档进行通信,从而实现了在网页中使用 ADB 工具的能力,那么我第一个发现奇怪的现象便是其实 USBDevice 对象是连接上了的,那么证明底层的 USB 是没有问题的,因为排除掉 WebUSB 问题后,我就开始研究 ADB 数据协议。

需要学习的可以这里阅读: android.googlesource.com

首先,既然 WebUSB 数据没有问题,那我就通过 debugger 大法在 读取数据的地方 加了断点,调试过程中发现连续两次都进是以 AdbCommand.Auth 的结果来进行解析的,并没有收到 AdbCommand.Connect ,并且这里没有超时机制,于是就导致了一开始所说的问题,没有报错,也没有返回。

在知道了这个信息后,我开始真正地看 ADB 数据协议了,这里就略过我看的过程,直接说结论好了,一般 ADB 两端在建立连接时(之前认证过)的流程如下:

  1. 发送方先发送一个“连接”请求
  2. 接收方收到后,回复一个随机串(20位),作为所谓的 Token
  3. 发送方收到 Token 后,需要对该串进行签名,并回发 Signature
  4. 如果此时失败,接收方会继续发送新的 Token 重试
  5. 如果认证成功(和设备端的钥匙进行对比),那么会进行后续操作

这里关键在操作在于第五步的比对,上述流程是对于授权过的设备,一般来说当一个新的 ADB 客户端要连接 Android 设备时,一般都会在手机端弹出一个是否授权的提示,点击是后,其实就会在手机端将本次授权的相关信息记录在本地,与此同时,ADB 客户端也需要记录本次的认证信息,以便下次连接使用,这样就不用每次都需要用户进行授权了。

而问题就出在这里,在 WebADB 的实现中,是通过 localStorage 来存储首次的认证信息的, 点击查看代码 ,巧就巧在 WebUSB 和 localStorage 所定义的安全域并不一样。

在 localStorage 中,相同的 Host 不同的端口之间被认为是跨域,因此互相并不能够共享存储内容,而在 WebUSB 中,尽管标准中提到了安全域的范围是 Origin,而 Origin 中包含了 Host 和 Port,但在浏览器中,使用不同的端口(Port),却被认为是同一个客户端。

这也许是 Chrome 的一个 Bug,而现在可以通过在安卓手机中的开发者选项中,擦除所有 USB Debugging 认证信息,不过还是希望 Chrome 尽快修复这个问题,减少开发者遇到的困惑。

发布于 2022-02-18 04:32