Android发热监控实践( 二 )

但对于发热等级来说,壳温无疑是最为能够反应手机的发热情况的 。可以看到 Android 系统的 API 实际上是提供了 AIDL 接口,可以直接注册 Thermal 变更事件的监听 , 获取到 Temperature 对象 。但由于标识了 Hide API。常规应用层是无法获取到的,在考虑好 Android 版本兼容性前提下,通过反射代理 ThermalManagerService 方式进行读取 。
 

Android发热监控实践

文章插图
图片
但事与愿违,国内厂商并没有完全适配官方热缓解框架,热状态回调时常不够准确,而是需要单独接入每个厂商的热缓解 SDK 去直接获取到壳温,具体 API 则以各应用厂商的内部接入文档为准 。
CPU使用率CPU 使用率的采集通过读取解析 Proc stat 文件的方式进行计算 。在系统 proc/[pid]/stat  和  /proc/[pid]/task/[tid]/stat  分别记录了对应进程 ID、进程 ID 下的线程 ID 的 CPU 信息 。具体的字段描述在此不进行赘述,详见:https://man7.org/linux/man-pages/man5/procfs.5.html 。
Android发热监控实践

文章插图
图片
我们重点关注 14.15 位的信息 , 分别代表进程/线程的用户态运行的时间和内核态运行的时间 。
Android发热监控实践

文章插图
图片
通过解析当前进程的 Stat 文件,以及 Task 目录下所有线程的 Stat 文件 , 在两次采样周期内(当前设置为 1s)的 utime+stime 之和的差值/采样间隔,即可认为是进线程的 CPU 的使用率 。即 进线程 CPU 使用率 = ((utime+stime)-(lastutime+laststime)) / period
GPU使用率高通芯片的设备,我们可以参考 /sys/class/kgsl/kgsl-3d0/gpubusy 下文件内容,参考高通官网的说明 。GPU 的使用率 = (下图)数值 1 / 数值 2 * 100,经过验证与 SnapDragonProfiler 信息采集获取的数值基本一致 。
联发科芯片的设备,我们可以直接通过读取 /d/ged/hal/gpu_utilization 下的使用率数值 。
同样的通过指定周期(每秒 1 次)的采样间隔,即可获取到每秒的当前 GPU 使用率 。
系统服务使用Android 系统服务包括 Warelock、Alarm、Sensor、wifi?.NET、Location、Bluetooth、Camera等 。与市面上常规的监控手段差异不大,都是通过系统 Hook ServiceManager 的方式 , 监听系统服务的 Binder 通信 , 匹配对应的调用方法名,做对应中间层监控的回调记录处理 。
熟悉 Android 开发的同学知道 Android 的 Zygote 进程是 Android 系统启动时的第一个进程 。在 Zygote Fork 进程中会孵化出系统服务相关的进程 SystemServer , 在其核心的 RUN 方法中,会注册启动大量的系统服务,并通过 ServiceManager 进行管理 。
Android发热监控实践

文章插图
故我们可以通过反射代理 ServiceManager 的方式,以 LocationManager 为例进行监听,拦截对应 LocationManager 内对应的方法,记录我们期望获取的数据 。
// 获取 ServiceManager 的 Class 对象Class<?> serviceManagerClass = Class.forName("android.os.ServiceManager");// 获取 getService 方法Method getServiceMethod = serviceManagerClass.getDeclaredMethod("getService", String.class);// 通过反射调用 getService 方法获取原始的 IBinder 对象IBinder originalBinder = (IBinder) getServiceMethod.invoke(null, "location");// 创建一个代理对象 ProxyClass<?> iLocationManagerStubClass = Class.forName("android.location.ILocationManager$Stub");Method asInterfaceMethod = iLocationManagerStubClass.getDeclaredMethod("asInterface", IBinder.class);final Object originalLocationManager = asInterfaceMethod.invoke(null, originalBinder);Object proxyLocationManager = Proxy.newProxyInstance(context.getClassLoader(),new Class[]{Class.forName("android.location.ILocationManager")},new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 在这里进行方法的拦截和处理Log.d("LocationManagerProxy", "Intercepted method: " + method.getName());// 执行原始的方法return method.invoke(originalLocationManager, args);}});// 替换原始的 IBinder 对象getServiceMethod.invoke(null, "location", proxyLocationManager);同理 我们获取在固定采样周期内 各系统服务对应 申请次数、计算间隔时长等进行记录 。
源码 Power_profile 文件中定义了每个系统服务状态下的电流量定义 。
我们在需要记录每个元器件在不同状态的工作时间之后,通过以下计算方式,可以得出元器件的发热贡献排行 , 即:


推荐阅读