使用ADB获取安卓任务缩略图
前言
参考的文章
- https://blog.csdn.net/a462533587/article/details/105784151
- https://www.twblogs.net/a/5b8caad22b71771883346a0a
通过 ADB 获取安卓任务截图,这篇文章可能是 2022 年唯一能用的方案,目前测试了安卓12和安卓11,根据 shell.apk 这个系统 apk 自带的权限,想在任何版本中获取到后台任务的缩略图,应该都是可行的。
我加了可能哈
起初不太想写有关这类的文章,但竟没想到实现这样一个简单的东西竟如此坎坷,正当我海量浏览 Google 来的资料时,学校啪的一下给我寝室电断了。
感觉翻了整个google,唯一有用的文章是
首先 AcitvityManager,DisplayManager 这些都是安卓的 services,通过 context 的 getSystemService 传入 key 即可获取,而这一系列的 manager 其实都是 wrapper,在设计模式中叫装饰器模式,内部其实都是调用的对应的 ManagerServices,而这个 Services,可以反射出来,这部分的知识来自 scrcpy 的 server,代码参考ServiceManager.java,所以在没有 app 运行的 dex 中,我们可以反射出我们想要使用的 Services。
安卓任务的缩略图保存在 ,但是文件及文件夹的上下文都是 system,即使是 shell,也没法直接通过文件读写获取到缩略图。
开始分析
为了检测在 java runtime 中反射出的对象,是否有我们需要的函数,写了一个简单的打印类的工具,如下
文中所指向 getTaskSnapshot 这个方法是来自ActivityManager.getService() 内的,但是,随着安卓的更新,这个函数换!位!置!了!但是这篇文章给到我很大的启示。
在IActivityManager.java中还有 getTaskThumbnail 方法,后来也没了。
再想一下,在我们的安卓设备中,哪一个app是能够获取到后台任务缩略图的?
那就是系统自带任务管理,这个任务管理无非权限高一点,它依然是一个独立的安卓 app,只要它能获取到,我们在 shell 上下文的加持下,也一样能获取到。
后来直接从安卓源码入手,基于以前的反编译经验,可以很快知道安卓的后台管理页面都在 SystemUI.apk 中 ,在安卓中对应的包就是SystemUI
通过 github 的代码搜索,我们也能很快能在ActivityManagerWrapper.java 找到 getTaskThumbnail 函数,
1 | /** |
关键代码如下
1 | snapshot = ActivityTaskManager.getService().getTaskSnapshot(taskId, isLowResolution); |
我们发现 getTaskSnapshot 这个函数了来自 ActivityTaskManager 的 Services ,通过源代码ActivityTaskManager.java 我们可以找到 getService() 函数,是一个静态函数,也就是说我们可以直接反射来用。
如下
1 | Class<?> cls = Class.forName("android.app.ActivityTaskManager"); |
这个 itm 其实就是一个 IActivityTaskManager。而我们再通过反射工具类来看一下它的函数
的确是有 getTaskSnapshot 函数的,接着我们获取 TaskSnapshot 对象
如下
研究到这儿我以为已经结束了,但我拿到的是一个 TaskSnapshot 对象,源码定义在TaskSnapshot.java,那我们怎么获取图片字节流呢?
构造Bitmap
我们顺利的通过反射得到 TaskSnapshot 后,但是如何借助 ADB 将这个玩意传出去呢,看了它所有的属性,好像都用不上,于是我开始尝试将它转换为 bitmap 再获取字节流。经过上面文章的指引,我开始尝试构造一个bitmap,当我把文中的这行代码放到我的IDE后
1 | Bitmap bmp = Bitmap.createHardwareBitmap(buffer); |
很不意外的报错了,有可能是 SDK 版本导致的 api 的 break change,但是最后发现 Bitmap 有一个 wrapHardwareBuffer 函数,原型如下
1 | public static Bitmap wrapHardwareBuffer(@NonNull HardwareBuffer hardwareBuffer, @Nullable ColorSpace colorSpace); |
于是我开始找 TaskSnapshot 中可用的属性。
1 | @UnsupportedAppUsage |
其中 getHardwareBuffer 函数在安卓11没有安卓12有。
为了兼容,使用一下代码反射出对应的对象
1 | Object buffer = snapshot.getClass().getMethod("getSnapshot").invoke(snapshot); |
最后再构造一个 bitmap
创建服务端
我们能够很轻松的通过 adb 建立端口的转发。
最后基于 HTTP 实现,使用NanoHttp这个微型框架,来提供可以获取缩略图的服务端。
将 java 代码编译成 dex 后,push 到设备上再用 app_process 执行,服务就启动了,用浏览器就能直接获取任务缩略图。
如图
最后,一个分布式的任务管理器不就来了吗。
在电脑上管理手机应用
在手机上管理另一台手机的应用
之前看到过的写得很好的一句话
“这就是所谓的劣币驱逐良币罢。写一篇好的技术文章需要大量的成本取找资料去实
践,而且写得越深入,门槛就越高,受众就越小。
然而水一篇晒工资,晒房子,感情八卦的文章却没
什么难度,而且最能戳中每个人心中的 g 点,或
意淫幻想,或愤愤不平。
然后这种事情如同滚雪球一样,越滚越大,然后吸
引一些臭味相投的人。
这是个娱乐至死的年代,一个逐利的社区为了流量,自然需要这样的内容来充实自己的运营数据。掘金
如此,知乎亦如此。”
技术不应该只是为了流量,技术的价值在于技术本身~
使用ADB获取安卓任务缩略图