概述
由手机性能评测--3D场景可以知道,要想获取surfaceflinger中的fps值,要么是SHELL权限,要么就是拥有android.Manifest.permission.DUMP权限的应用(系统应用)。但是作为一个三方app,GameBench却可以获得任意程序的fps值,着实让人觉得不明觉厉。以下是运行时的截图,跑的是Unity3d写的demo。

分析
因为只有2条路可以拿到fps值,而DUMP权限那条是根本行不通的,因为三方app是不可能有系统签名的何况还是这么多个系统。所以真相应该只有一个,就是通过shell权限那条路拿到的。
进程分析
一个三方app如何能以shell权限运行呢?答案当然是: 不可能
想要获取进程之间的秘密,还是先让我们来看看启动GameBench后,多了哪些进程吧。
在运行GameBench前,使用命令adb shell ps | tee xxx/xxx/before.pid将所有的进程信息保存到before.pid文件中
然后在打开GameBench,并启动我们的Unity3d demo,当fps显示出来后再使用命令adb shell ps | tee xxx/xxx/after.pid保存所有的进程信息
然后使用对比工具BeyondCompare对比前后2支文件,接下来就是见证奇迹的时刻。
除了多了我们的Unity3d进程外,还多了如下的一个进程
shell 9007 1 18652 3992 ffffffff f74bd9ac S /data/local/tmp/gbhelperdaemon
是不是很激动?让我们来看看这个gbhelperdaemon是什么东西
gbhelperdaemon
从名字看,这应该是一个daemon程序,gb就是gamebench的缩写
我们知道它的位置在/data/local/tmp/ 我们先看看它是什么
-
首先查看权限
ls -l /data/local/tmp/gbhelperdaemon
输出结果如下-rwxrwxrwx shell shell 34276 2016-08-23 15:13 gbhelperdaemon注意:这里是777的权限,并且owner和group都是shell
-
先使用如下命令将gbhelperdaemon pull出来
adb pull /data/local/tmp/gbhelperdaemon xxx/xxx/ - 查看它的格式
输出结果如下file xxx/xxx/gbhelperdaemon
可以看出这是一个32位的ELF文件/home/hly/work/gbhelperdaemon: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped - 以文本的方式编辑该文件,给出部分重要截图

gb_efl_screenshot.png

gb_efl_screenshot2.png
这里有4个关键的地方已经用红色对话框标示出来。这个ELF文件应该至少会做3件事,首先开启了一个线程,然后使用了socket或者pipe来进行RPC。RPC传递的数据就是使用的dumpsys surfaceflinger --latency - gen.sh
查看/data/local/tmp/目录 ,你还会发现多了一个文件叫做gen.sh,内容很简单#!/bin/sh /data/local/tmp/gbhelperdaemon &
推测
综上所述,gamebench的实现原理应该是这么的
- 首先将gen.sh和gbhelperdaemon push到手机里面
- 运行gen.sh脚本来启动daemon程序(或者直接启动daemon程序)
- daemon程序会一直运行,并且等待其他socket来连接以便发送数据
- 当我们运行gamebench这个app的时候,app会和daemon程序建立连接
- 因为gbhelperdaemon 是拥有shell权限的,所以可以使用dumpsys surfaceflinger --latency来获取数据并计算出fps
- 建立连接后,daemon程序会将获得的数据发送到我们的app端,app再显示出来。
疑问
那gen.sh和gbhelperdaemon是何时被放入到手机里面的呢?
还记得第一次运行gamebench时,(gamebench setup,看视频需翻墙)会提示让你先运行一个gamebench的桌面程序吗?
那个程序应该就是把脚本和elf文件放进了手机里面,并且启动了daemon程序。
验证
- 杀掉/data/local/tmp/gbhelperdaemon进程
当正常运行gamebench的时候,杀掉gbhelperdaemon进程后,gamebench的fps一直显示为60。所以app是没办法获得fps数据,只能是daemon进程传递过来的 - 关掉gamebench app直接运行gen.sh,会得到如下结果

可以看出来,daemon是一直在等待connection的 - 杀掉daemon,打开app
当杀掉damon运行app的时候,会让你把手机连接电脑运行gb的一个桌面程序,才能继续运行。
这个时候直接执行
开启deamon程序,就可以不用再运行gb的桌面程序,并且会有如下的一些log输出sh /data/local/tmp/gen.sh
总结
gamebench的这种工作方式,不仅仅可以拿到fps还能拿到全部shell权限能访问的信息。运行在界面上的app就只是相当于一个外壳,包装的是shell。