vysor原理以及Android同屏方案
vysor是一个免root实现电脑控制手机的chrome插件,目前也有几款类似的通过电脑控制手机的软件,不过都需要root权限,并且流畅度并不高。vysor没有多余的功能,流畅度也很高,刚接触到这款插件时我惊讶于它的流畅度以及免root,就一直对它的实现原理很感兴趣。这款插件我用了大半年,最近在升级后我发现它居然开始收费了,终生版需要39.99美元,不过经过简单的分析后我很轻松的破解了它的pro版,在分析的过程中发现它的原理并不复杂,所以就打算自己也实现一个类似的软件。
截屏常见的方案
在介绍vysor的原理前我先简单介绍一下目前公开的截屏方案。
- View.getDrawingCache()
这是最常见的应用内截屏方法,这个函数的原理就是通过view的Cache来获取一个bitmap对象,然后保存成图片文件,这种截屏方式非常的简单,但是局限行也很明显,首先它只能截取应用内部的界面,甚至连状态栏都不能截取到。其次是对某些view的兼容性也不好,比如webview内的内容也无法截取。
- 读取/dev/graphics/fb0
因为Android是基于linux内核,所以我们也能在android中找到framebuffer这个设备,我们可以通过读取/dev/graphics/fb0这个帧缓存文件中的数据来获取屏幕上的内容,但是这个文件是system权限的,所以只有通过root才能读取到其中的内容,并且直接通过framebuffer读取出来的画面还需要转换成rgb才能正常显示。下面是通过adb读取这个文件内容的效果。
- 反射调用SurfaceControl.screenshot()/Surface.screenshot()
SurfaceControl.screenshot()(低版本是Surface.screenshot())是系统内部提供的截屏函数,但是这个函数是@hide的,所以无法直接调用,需要反射调用。我尝试反射调用这个函数,但是函数返回的是null,后面发现SurfaceControl这个类也是隐藏的,所以从用户代码中无法获取这个类。也有一些方法能够调用到这个函数,比如重新编译一套sdk,或者在源码环境下编译apk,但是这种方案兼容性太差,只能在特定ROM下成功运行。
- screencap -p xxx.png/screenshot xxx.png
这两个是在shell下调用的命令,通过adb shell可以直接截图,但是在代码里调用则需要系统权限,所以无法调用。可以看到要实现类似vysor的同步操作,可以使用这两个命令来截取屏幕然后传到电脑显示,但是我自己实现后发现这种方式非常的卡,因为这两个命令不能压缩图片,所以导致获取和生成图片的时间非常长。
- MediaProjection,VirtualDisplay (>=5.0)
在5.0以后,google开放了截屏的接口,可以通过”虚拟屏幕”来录制和截取屏幕,不过因为这种方式会弹出确认对话框,并且只在5.0上有效,所以我没有对这种方案做深入的研究。
可以看到,上述方案中并没有解决方案能够做到兼容性和效率都非常完美,但是我在接触到vysor后发现它不但画面清晰,流畅,而且不需要root。那么它是用了什么黑科技呢?下面我们反编译它的代码来研究一下它的实现机制。
vysor原理
反编译vysor的apk后可以发现它的代码并不多,通过分析后我发现它的核心代码在Main这个类中。
首先来看Main函数的main方法,这个方法比较长,这里直接贴出源码。
1 |
public static void main(String[] args) throws Exception { |
这个软件koushikdutta是由开发的,这个团队以前发布过一个非常流行的开源网络库:async。在这个项目中也用到了这个开源库。main函数主要是新建了一个httpserver然后开放了几个接口,通过screenshot.jpg获取截图,通过socket input接口来发送点击信息,通过h264这个接口来获取实时的屏幕视频流。每一个接口都有对应的响应函数,这里我们主要研究截图,所以就看screenshot这个接口。h264这个接口传输的是实时的视频流,所以就流畅性来说应该会更好,它也是通过virtualdisplay来实现的有兴趣的读者可以自行研究。
接下来我们来看screenshot对应的响应函数AnonymousClass5的实现代码。
1 |
* renamed from: com.koushikdutta.vysor.Main.5 */ |
这个类传入了一个wm类,这个类是用来监听屏幕旋转的,这里不用管它。另外在vysor开始运行时,会随机生成一个验证码,只有验证通过才能进行连接,所以这里有一个验证的过程,这里也不过管。可以看到这个类定义的响应函数的代码非常简单,就是通过EncoderFeeder.screenshot()函数来过去截图的bitmap,然后返回给请求端。那么EncoderFeeder.screenshot这个函数是怎样实现截图的呢?
1 |
public static Bitmap screenshot(IWindowManager wm) throws Exception { |
这里的截图的核心代码也是反射调用Surface/SurfaceControl的screenshot方法。但是我们前面已经了解到,这个类只有在系统权限下才能获取到,那么vysor又是怎么调用到这个函数的呢?我们可以确认的是vysor不是通过重编译sdk和使用系统签名来完成的,因为那样只能对特定的rom适用。
当时看到这里的代码后我也非常困惑,vysor是怎么调用到这个类的。我注意到了vysor的核心代码不是在某个Activity或者Service中而是在一个Main类中,按照一般的逻辑来说,这种实时传屏应该是放在Service中不断截屏然后发给服务端,所以我决定再看下它的服务端的代码。
vysor的服务端是一个chrome插件,用javascript写成的,所以找到源码比java更加简单。虽然js经过混淆,但是很容易的可以通过一些工具来解密。然后就是分析它的代码了,终于被我找到了关键的代码。
1 |
function y(e, t, n) { |
可以看到上面的代码是调用了adb shell命令来启动com.koushikdutta.vysor.Main类,并且上面获取了app_process这个程序。相信对android熟悉读者已经明白它的原理了。我简单解释一下。我们已经知道Surface/SurfaceControl这两个类是需要具有相应权限的程序才能调用到,用户进程无法获取到。adb shell可以调用screencap或者screenshot来截取屏幕,那就说明adb shell具有截屏的权限。Surface/SurfaceControl和screenshot/screencap它们内部的实现机制应该是相同的,所以也就是说adb shell是具有截屏权限的也就是能够调用到Surface/SurfaceControl。那么我们怎么通过adb shell来调用到这两个类呢,答案就是app_process。app_process可以直接运行一个普通的java类,详细的资料大家可以在网上找到。也就是说我们通过adb shell运行app_process,然后通过app_process来运行一个java类,在java类中就可以访问到Surface/SurfaceControl这两个类,是不是很巧妙?
理论有了,下面我们来通过代码验证。这里我们可以直接使用vysor的代码。因为是测试用所以我没有添加其他功能。
1 |
public class Main { static Looper looper; public static void main(String[] args) { AsyncHttpServer httpServer = new AsyncHttpServer() { |
编译成apk然后安装后,我们使用adb shell来运行这个类,主要方法如下,首先导出classpath,否则会提示找不到类。
1 |
export CLASSPATH=/data/app/com.zke1e.andcast-1/base.apk |
然后调用app_process来启动这个类。
1 |
exec app_process /system/bin com.zke1e.andcast.Main '$@' |
可以看到类已经成功运行了,正在监听请求。
然后使用adb forward转发端口。
1 |
adb forward tcp:53516 tcp:53516 |
最后在浏览器里访问,就可以获取截图了。
当然只有简单的截图功能是不够,我们需要能够流畅实时的传输android的屏幕,并且能够在电脑上控制,经过两天的编写,我使用java实现了类似vysor的功能。从流畅度和清晰度上都和vysor差不多,后续还会考虑加入文件传输和声音传输等功能。最近计划编写一个java版的android反编译集成环境,类似android killer。因为android killer只能在windows上使用,而linux下没有类似的方面的软件。到时这个同步软件可以作为插件和反编译套件集成。最后放一张截图。
更新
经过一段时间的研究,最后实现了将传输的截图改成了h264码流,提高的流畅度和稳定性,然后将接受端放在了浏览器中,实现了可以在浏览器中对android手机进行控制,下面是截图。
vysor原理以及Android同屏方案的更多相关文章
- 【转】Android 全屏方案(隐藏NavigationBar)
http://www.07net01.com/2015/04/822292.html 在android4.0及其以上的版本中,出现了一个很屌的东西,叫做Navigation Bar,它和Status ...
- 全面盘点当前Android后台保活方案的真实运行效果(截止2019年前)
本文原作者“minminaya”,作者网站:minminaya.cn,为了提升文章品质,即时通讯网对内容作了幅修订和改动,感谢原作者. 1.引言 对于IM应用和消息推送服务的开发者来说,在Androi ...
- 《推送开发全面盘点当前Android后台保活方案的真实运行效果》
登录 立即注册 TCP/IP详解 资讯 动态 社区 技术精选 首页 即时通讯网›专项技术区›推送开发全面盘点当前Android后台保活方案的真实运行效果(截止2 ... 帖子 打赏 分 ...
- fir.im Weekly - iOS / Android 动态化更新方案盘点
动态化更新是 App 开发必然面对的问题.在 iOS 环境下,Apple 开发者们像是" 带着手铐脚镣跳舞" ,相比之下 Android 开发者会轻松一点,有很多相关的开源框架帮助 ...
- Android横竖屏切换小结
Android横竖屏切换小结 (老样子,图片啥的详细文档,可以下载后观看 http://files.cnblogs.com/franksunny/635350788930000000.pdf) And ...
- 【转】Android横竖屏切换问题
Android横竖屏切换总结(Android资料) Android横竖屏要解决的问题应该就两个: 一.布局问题 二.重新载入问题 1.布局问题:如果不想让软件在横竖屏之间切换,最简单的办法就是在项目的 ...
- Android横竖屏切换总结
Android横竖屏切换总结(Android资料) Android横竖屏要解决的问题应该就两个: 一.布局问题 二.重新载入问题 1.布局问题:如果不想让软件在横竖屏之间切换,最简单的办法就是在项目的 ...
- 【转】android 电容屏(三):驱动调试之驱动程序分析篇
关键词:android 电容屏 tp 工作队列 中断 坐点计算 电容屏主要参数平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台:S5PV310( ...
- 【转】android 电容屏(二):驱动调试之基本概念篇
关键词:android 电容屏 tp 工作队列 中断 多点触摸协议平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台:S5PV310(samsung ...
随机推荐
- 如何在主Form出现之前,弹出密码验证From,Cancel就退出程序,Ok后密码正确才出现主Form
如何在主Form出现之前,弹出密码验证From,Cancel就退出程序,Ok后密码正确才出现主Form本文地址 :CodeGo.net/5175478/ ----------------------- ...
- Tree Constructing CodeForces - 1003E(构造)
题意: 就是让构造一个直径为d的树 每个结点的度数不能超过k 解析: 先构造出一条直径为d的树枝 然后去遍历这条树枝上的每个点 为每个点在不超过度数和直径的条件下添加子嗣即可 #include & ...
- Berland and the Shortest Paths CodeForces - 1005F(最短路树)
最短路树就是用bfs走一遍就可以了 d[v] = d[u] + 1 表示v是u的前驱边 然后遍历每个结点 存下它的前驱边 再用dfs遍历每个结点 依次取每个结点的某个前驱边即可 #include &l ...
- 特殊符号 UNICODE编码
特殊符号 UNICODE编码 =================== Start ⇠ 箭头类 符号 UNICODE 符号 UNICODE HTML JS CSS HTML JS CSS ⇠ & ...
- 【BZOJ2257】[JSOI2009]瓶子和燃料(数论)
[BZOJ2257][JSOI2009]瓶子和燃料(数论) 题面 BZOJ 洛谷 题解 很明显就是从\(n\)个数里面选\(K\)个数让他们的\(gcd\)最大. 暴力找所有数的因数,拿个什么东西存一 ...
- GDOI2018 Day1 题目总结
T1:农场 题意:有一个长为 $n$ 的序列 $a$,要求将其分成尽可能多的部分,使得每一部分的 $a_i$ 的和相等.求最多能分成的部分数. $30\%:1\le n\le 1000$ $80\%: ...
- (转)面向对象——UML类图设计
背景:一直以来,对UMl类图的画法不甚理解,但是随着学习的深入,发现熟练掌握UML类图,能够更好理解代码间的逻辑性,而这也是程序设计的基础所在,所以很有必要把UML好好掌握. UML类图新手入门级介绍 ...
- linux c 编程 ------ 串口编程
http://blog.csdn.net/specialshoot/article/details/50707965 对于串口的打开操作,必须使用O_NOCTTY参数.O_NOCTTY如果路径名指向终 ...
- Saltstack-API(十二)
Saltstack-API 官方文档 https://docs.saltstack.com/en/latest/ref/netapi/all/salt.netapi.rest_cherrypy.htm ...
- Java基础-处理json字符串解析案例
Java基础-处理json字符串解析案例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 作为一名开发人员,想必大家或多或少都有接触到XML文件,XML全称为“extensible ...