news 2026/4/15 22:20:21

AOSP 客制化内功心法(五)让状态栏显示 CPU 温度 —— 从 Kernel 到 SystemUI 的数据流动艺术

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AOSP 客制化内功心法(五)让状态栏显示 CPU 温度 —— 从 Kernel 到 SystemUI 的数据流动艺术

🌡️ 引言:为什么“显示 CPU 温度”是绝佳学习案例?

  • 涉及层级最全:Kernel → HAL → SystemService → Framework → SystemUI
  • 数据流清晰:单向读取(非控制类),逻辑简单但完整
  • 贴近真实需求:性能监控、过热预警、调试工具等场景高频使用

更重要的是——

它完美展示了 AOSP 中“被动采集 + 主动推送 + UI 响应”的三段式数据流动模型

今天,我们就用“搭建一条体温监测流水线”的比喻,手把手打通这条链路!


🏗️ 整体架构:五层数据流水线

层级角色类比
Kernel体温传感器医院的红外测温仪
HAL数据采集员护士,定时读取体温
SystemService健康数据中心医生办公室,汇总数据
Framework对外窗口挂号台,提供查询接口
SystemUI显示屏病房门口的电子体温牌

✅ 我们的目标:让“电子体温牌”实时显示最新体温!


第一章:第 1 步 —— Kernel 提供“体温数据源”

大多数 Android 设备通过thermal sysfs暴露 CPU 温度:

# 查看 CPU 温度(单位:毫摄氏度) cat /sys/class/thermal/thermal_zone0/temp # 输出:42000 → 表示 42.0°C

💡 不同设备路径可能不同(如thermal_zone7),可通过:

adb shell ls /sys/class/thermal/thermal_zone*/type

找到类型为cpu-*CPU的 zone。

本教程假设路径为/sys/class/thermal/thermal_zone0/temp
无需修改 Kernel,直接读即可!


第二章:第 2 步 —— HAL 层:招聘“体温护士”

📜 步骤 1:定义护士岗位说明书(HIDL)

创建:hardware/interfaces/thermal/1.0/IThermal.hal

package android.hardware.thermal@1.0; interface IThermal { // 获取 CPU 温度(单位:摄氏度) getCpuTemperature() generates (float temperature); }

🛠️ 步骤 2:实现护士工作(C++)

hardware/interfaces/thermal/1.0/default/Thermal.cpp

#include <fstream> #include <string> #include <android-base/strings.h> Return<float> Thermal::getCpuTemperature() { std::ifstream tempFile("/sys/class/thermal/thermal_zone0/temp"); std::string line; if (std::getline(tempFile, line)) { // 转换:42000 → 42.0 int milliTemp = std::stoi(line); return static_cast<float>(milliTemp) / 1000.0f; } return -1.0f; // 错误值 }

✅ 护士只做一件事:读文件 → 转换单位 → 返回结果


🚪 步骤 3:让护士上岗(启动服务)

android.hardware.thermal@1.0-service.rc

service vendor.thermal-hal /vendor/bin/hw/android.hardware.thermal@1.0-service class hal user system

编译后,系统开机自动运行,护士就位!


第三章:第 3 步 —— SystemService:建立“健康数据中心”

🏢 创建 ThermalService(医生办公室)

frameworks/base/services/core/java/com/android/server/ThermalService.java

public class ThermalService extends IThermalService.Stub { private IThermal mHal; public ThermalService() { try { mHal = IThermal.getService(); // 找到护士 } catch (Exception e) { Slog.e("ThermalService", "找不到体温护士", e); } } @Override public float getCpuTemperature() { if (mHal != null) { try { return mHal.getCpuTemperature(); // 问护士要数据 } catch (Exception e) { Slog.e("ThermalService", "获取体温失败", e); } } return -1.0f; } }

📋 在 SystemServer 中注册

SystemServer.java

ThermalService thermal = new ThermalService(); ServiceManager.addService("thermal", thermal); // 登记进通讯录 Slog.i(TAG, "Thermal Service started");

✅ 现在,任何系统组件都能通过ServiceManager.getService("thermal")查询体温!


第四章:第 4 步 —— Framework:设立“挂号窗口”

📖 定义对外接口(AIDL)

frameworks/base/core/java/android/os/IThermalService.aidl

interface IThermalService { float getCpuTemperature(); }

👩‍💼 在 ContextImpl 中注册窗口

ContextImpl.java

registerService(Context.THERMAL_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService("thermal"); return IThermalService.Stub.asInterface(b); } });

🏷️ 声明服务常量

Context.java

public static final String THERMAL_SERVICE = "thermal";

✅ 现在,App 或 SystemUI 可以通过getSystemService(Context.THERMAL_SERVICE)获取窗口!


第五章:第 5 步 —— SystemUI:安装“电子体温牌”(状态栏)

这才是最精彩的部分!如何把数据画到状态栏?

🔧 步骤 1:创建温度监控器(TemperatureController)

frameworks/base/packages/SystemUI/src/com/android/systemui/thermal/TemperatureController.java

public class TemperatureController { private final IThermalService mService; private float mCurrentTemp = -1; private final List<Callback> mCallbacks = new ArrayList<>(); public TemperatureController(Context context) { mService = IThermalService.Stub.asInterface( ServiceManager.getService(Context.THERMAL_SERVICE) ); startMonitoring(); // 启动定时轮询 } private void startMonitoring() { Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> { try { float temp = mService.getCpuTemperature(); if (temp != mCurrentTemp) { mCurrentTemp = temp; notifyChange(); // 通知 UI 更新 } } catch (Exception e) { Log.w("TemperatureController", "读取温度失败", e); } }, 0, 2, TimeUnit.SECONDS); // 每 2 秒查一次 } public void addCallback(Callback cb) { mCallbacks.add(cb); } private void notifyChange() { for (Callback cb : mCallbacks) cb.onTemperatureChanged(mCurrentTemp); } public interface Callback { void onTemperatureChanged(float celsius); } }

💡 为什么用轮询而不是监听?
因为 Kernel不会主动通知温度变化!只能定期查。


🖼️ 步骤 2:创建状态栏图标(TemperatureIcon)

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/TemperatureIcon.java

public class TemperatureIcon extends StatusBarIconView { private TextView mTextView; public TemperatureIcon(Context context) { super(context, null, 0); mTextView = new TextView(context); mTextView.setTextSize(12); mTextView.setTextColor(Color.WHITE); addView(mTextView); } public void updateTemperature(float celsius) { if (celsius > 0) { mTextView.setText(String.format("CPU: %.0f°C", celsius)); setVisibility(VISIBLE); } else { setVisibility(GONE); } } }

🔌 步骤 3:把图标插入状态栏

修改StatusBar.java(简化版):

// 在 makeStatusBarView() 中 TemperatureIcon tempIcon = new TemperatureIcon(mContext); statusBar.addView(tempIcon); // 绑定控制器 TemperatureController controller = Dependency.get(TemperatureController.class); controller.addCallback(temp -> tempIcon.updateTemperature(temp));

✅ 注意:实际需通过Dependency注入或SysUIComponent初始化,此处简化。


第六章:权限与 SELinux(别让安全机制拦住你!)

🔒 1. 添加系统权限(可选)

若仅 SystemUI 使用,无需额外权限。
若 App 也要用,需在AndroidManifest.xml声明:

<uses-permission android:name="android.permission.ACCESS_THERMAL_DATA" />

并在platform.xml授权。

🔐 2. SELinux 放行 HAL 访问 sysfs

device/<vendor>/sepolicy/vendor/file_contexts

/sys/class/thermal/thermal_zone[0-9]+/temp u:object_r:sysfs_thermal:s0

thermal_hal.te

allow thermal_hal sysfs_thermal:file read;

✅ 否则 HAL 会因权限拒绝而返回 -1!


第七章:编译、刷机、验证

🛠️ 编译命令

source build/envsetup.sh lunch aosp_arm64-userdebug make -j$(nproc) SystemUI services thermal-hal

🔍 验证步骤

  1. 启动模拟器或真机
  2. 查看日志:
    adb logcat | grep -E "(Thermal|Temperature)"
  3. 观察状态栏右上角是否出现CPU: 42°C
  4. adb shell cat /sys/class/thermal/.../temp对比数值

💡 若模拟器无 thermal 数据,可用脚本模拟:

echo 50000 > /sys/class/thermal/thermal_zone0/temp

第八章:优化建议(从能用到好用)

问题优化方案
轮询耗电改用inotify监听 sysfs 文件变化(需 native 支持)
温度跳变加滑动平均滤波(如取最近 3 次平均值)
高温告警当 > 60°C 时变红闪烁
多核支持读取所有thermal_zone*取最高值

总结:数据流动的三大艺术

  1. 采集艺术(Kernel → HAL)
    → 用 sysfs 暴露硬件数据,HAL 封装读取逻辑。

  2. 服务艺术(HAL → SystemService → Framework)
    → 通过 HIDL + Binder 构建安全、跨进程的数据通道。

  3. 呈现艺术(Framework → SystemUI)
    → 用 Controller + Callback 解耦数据与 UI,实现响应式更新。

✅ 这套模式不仅适用于温度,还可用于:

  • 电池健康度
  • 网络信号强度
  • 自定义传感器数据
  • 系统负载(CPU 使用率)

🌈 结语:你已掌握 AOSP 数据流动的“任督二脉”

从前,你看到 SystemUI 里全是onXXX()updateState(),觉得“没人调用”。
现在,你知道:

每一个回调,都是数据流经的驿站;
每一个监听,都是系统在等待下一次心跳。

当你能在状态栏显示 CPU 温度,
你就真正理解了:
AOSP 不是代码的堆砌,而是数据的生命之河。


下一篇预告
《AOSP 客制化内功心法(六):
从零实现“勿扰模式快捷开关”——深入 Settings 与 Notification Manager 的联动机制》

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 5:29:49

LeetCode 461 - 汉明距离

文章目录摘要描述题解答案题解代码分析为什么一定要用异或&#xff1f;怎么数 1 的个数&#xff1f;Swift 可运行 Demo 代码代码逐步解析示例测试及结果与实际场景结合时间复杂度空间复杂度总结摘要 汉明距离这道题&#xff0c;属于那种题目极短、考点极准的类型。 表面看是在…

作者头像 李华
网站建设 2026/4/9 21:26:47

大数据领域元数据管理的开源工具推荐

大数据领域元数据管理的开源工具推荐关键词&#xff1a;大数据、元数据管理、开源工具、数据治理、数据血缘摘要&#xff1a;本文旨在为大家介绍大数据领域元数据管理的开源工具。在大数据时代&#xff0c;元数据管理就像是数据世界的地图&#xff0c;能帮助我们更好地理解和利…

作者头像 李华
网站建设 2026/4/13 10:36:55

大模型入门实战(非常详细)零基础入门到精通,收藏这一篇就够了

Part.1 什么是生成式AI&#xff1f; **“所有产品都值得用大模型重做一次。”**是近几年在AI圈子非常火爆的观点。 当大家都在热议大模型和生成式AI时&#xff0c;怎么让这些炫酷的技术快速落地&#xff0c;真正帮到商业和社会&#xff0c;成了个大难题。不过&#xff0c;AWS已…

作者头像 李华
网站建设 2026/4/10 7:25:25

【程序员必看】大模型本地化部署指南:macOS系统下LLM运行详解与收藏

本文详细介绍了大模型的基本概念、发展历程和技术原理&#xff0c;重点讲解了在macOS系统下本地运行大模型的实践方法。文章探讨了模型部署中的内存挑战和量化技术(GPTQ、GGML)&#xff0c;并通过llama.cpp和whisper.cpp等项目提供了具体的操作指南&#xff0c;帮助开发者在本地…

作者头像 李华
网站建设 2026/4/15 7:39:18

Conda list导出已安装包:Miniconda-Python3.10生成环境快照

Conda list导出已安装包&#xff1a;Miniconda-Python3.10生成环境快照 在科研、AI开发和工程部署中&#xff0c;你是否曾遇到过这样的场景&#xff1f;——同事发来一份PyTorch模型代码&#xff0c;你兴冲冲地运行&#xff0c;结果第一行就报错&#xff1a;“torch not found”…

作者头像 李华
网站建设 2026/4/15 7:41:29

PyTorch autograd机制解析:Miniconda-Python3.10调试梯度计算

PyTorch autograd机制解析&#xff1a;Miniconda-Python3.10调试梯度计算 在深度学习模型的开发过程中&#xff0c;一个看似微小的梯度异常就可能导致整个训练流程崩溃——你是否曾遇到过 loss 突然变为 NaN、参数毫无更新&#xff0c;甚至反向传播时程序静默失败&#xff1f;这…

作者头像 李华