1.前言
在10.0的系统定制化开发中,在开发某些产品的老化应用的时候,需要app获取cpu温度和电池 温度等功能,有些产品带温度传感器,大部分的产品都不包含温度传感器,所以就需要读取 sys下的相关节点来获取相关温度值
2.授权app获取cpu温度和电池温度功能实现的核心类
device/mediatek/sepolicy/basic/neverallows/non_plat/neverallows.te
device/mediatek/sepolicy/basic/neverallows/plat_public/neverallows.te
device/mediatek/sepolicy/bsp/non_plat/untrusted_app_27.te
system/sepolicy/prebuilts/api/30.0/private/app_neverallows.te
system/sepolicy/prebuilts/api/30.0/private/coredomain.te
system/sepolicy/private/app_neverallows.te
system/sepolicy/private/coredomain.te
3.授权app获取cpu温度和电池温度功能实现的核心功能分析和实现
在Android系统中,获取CPU温度的方法并没有直接提供给我们开发者,我们可以通过两种方式来获取Cpu温度: 1、 通过读取手机传感器 sensor 的温度近似于手机CPU温度(当然这种方式只是一个近似的值,并不准确,同时还需要手机具备相应的传感器) 2、 通过读取CPU信息来获取(这种方式相较于前一种方式获取到的数据准确很多,但是还是有一定的局限性。) 通过研究发现,CPU的信息基本都是在/sys/class/thermal/目录下,通过adb shell、cat相关命令可以拿到一些手机的CPU信息,基本步骤如下: 1、打开终端命令窗口,如windows下的cmd程序。 2、输入adb shell,回车。 3、输入cat /sys/class/thermal/thermal_zone7/type,回车。其中7只是一个示例,不同的手机可能会有区别。此命令可以获取相关的硬件类别。 4、输入cat /sys/class/thermal/thermal_zone7/temp,回车。就可以获取对应的温度值。 首选看下第一种方式,通过温度传感器来获取cpu温度的功能实现
SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);
for (Sensor sensor : sensorList) {
Log.e(TAG,"name:"+sensor.getName());
if (sensor.getName().contains("thermal-zone")) {
cpuTemperatureSensor = sensor;
break;
}
}
SensorEventListener cpuTemperatureListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
// 获取CPU温度
float temperature = event.values[0];
Log.e(TAG,"temperature:"+temperature);
// 处理温度数据
// ...
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// 温度精度变化回调
}
};
sensorManager.registerListener(cpuTemperatureListener, cpuTemperatureSensor, SensorManager.SENSOR_DELAY_NORMAL);
或者这样获取温度传感器:
for (Sensor s : allSensors) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
typeName = SensorTypeName.getSensorTypeName(s.getType()) + " " + s.getStringType();
if (s.getStringType().toUpperCase().indexOf("TEMP") > 0) {
// 可以看到,这里将包含有TEMP关键字的sensor付给了变量mTempSensor
// 而这个mTempSensor 就是我们需要的温度传感器
mTempSensor = s;
}
} else {
typeName = SensorTypeName.getSensorTypeName(s.getType()) + " " + s.getType();
}
sb.append(String.format("\t类型:%s\n", typeName));
sb.append(String.format("\t设备名称:%s\n", s.getName()));
sb.append(String.format("\t设备版本:%s\n", s.getVersion()));
sb.append(String.format("\t供应商:%s\n", s.getVendor()));
sb.append("\n");
}
// 这里我们将所有的传感器都放在一个subStr中,方便查看我们的结果
tx1.setText(sb.toString());
// 如果传感器不为空,那么我们就可添加一个监听,获取传感器的温度情况
if (mTempSensor != null) {
sm.registerListener(mSensorEventListener, mTempSensor
, SensorManager.SENSOR_DELAY_GAME);
}
// 温度传感器的监听器
private final SensorEventListener mSensorEventListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
if (event.sensor.getStringType().toUpperCase().indexOf("TEMP") > 0) {
/*温度传感器返回当前的温度,单位是摄氏度(°C)。*/
float temperature = event.values[0];
Log.e("temperature: ", temperature);
sm.unregisterListener(mSensorEventListener, mTempSensor);
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
};
通过发现好多手机设备不支持温度传感器,所以这样就获取不到当前的温度值,就需要读取 /sys/class/thermal/thermal_zone*/temp的值来判断当前的cpu温度的值,接下来看下 具体的代码实现
public List<String> getThermalInfo() {
List<String> result = new ArrayList<>();
BufferedReader br = null;
try {
File dir = new File("/sys/class/thermal/");
File[] files = dir.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
if (Pattern.matches("thermal_zone[0-9]+", file.getName())) {
return true;
}
return false;
}
});
final int SIZE = files.length;
String line = null;
String type = null;
String temp = null;
for (int i = 0; i < SIZE; i++) {
br = new BufferedReader(new FileReader("/sys/class/thermal/thermal_zone" + i + "/type"));
line = br.readLine();
if (line != null) {
type = line;
}
br = new BufferedReader(new FileReader("/sys/class/thermal/thermal_zone" + i + "/temp"));
line = br.readLine();
if (line != null) {
long temperature = Long.parseLong(line);
if (temperature < 0) {
temp = "Unknow";
} else {
temp = (float) (temperature / 1000.0) + "°C";
}
}
result.add(type + " : " + temp);
}
br.close();
} catch (Exception e) {
result.add(e.toString());
} finally {
if (br != null) {
try {
br.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return result;
}
在通过第二种方式来获取cpu温度和电池温度的时候,在app读取/sys/class/thermal/thermal_zone*/temp 的过程中,会出去读不到值,报selinux权限的问题,所以就需要根据报错信息来解决这个问题 首选可以通过adb命令禁用selinux来看是否能正常读取到cpu和电池温度,如果可以正常读取到 就证明需要根据log来授予selinux权限了,接下来看下具体的补丁
--- a/device/mediatek/sepolicy/basic/neverallows/non_plat/neverallows.te
+++ b/device/mediatek/sepolicy/basic/neverallows/non_plat/neverallows.te
@@ -40,6 +40,7 @@ full_treble_only(`
ueventd
vendor_init
vold
+ untrusted_app_27
} sysfs:file *;
--- a/device/mediatek/sepolicy/basic/neverallows/plat_public/neverallows.te
+++ b/device/mediatek/sepolicy/basic/neverallows/plat_public/neverallows.te
@@ -15,6 +15,7 @@ full_treble_only(`
-init
-ueventd
-vold
+ -untrusted_app_27
} sysfs:file *;
--- a/device/mediatek/sepolicy/bsp/non_plat/untrusted_app_27.te
+++ b/device/mediatek/sepolicy/bsp/non_plat/untrusted_app_27.te
@@ -12,3 +12,7 @@ get_prop(untrusted_app_27, vendor_mtk_atm_ipaddr_prop)
# Operation : eMBMS Migration
# Purpose :allow EXPWAY middleware to access the socket
allow untrusted_app_27 radio:unix_stream_socket connectto;
+allow untrusted_app_27 sysfs_therm:dir { search read open };
+allow untrusted_app_27 sysfs:dir { search read open };
+allow untrusted_app_27 sysfs_therm:file { open read getattr };
+allow untrusted_app_27 sysfs:file { open read getattr };
--- a/system/sepolicy/prebuilts/api/30.0/private/app_neverallows.te
+++ b/system/sepolicy/prebuilts/api/30.0/private/app_neverallows.te
@@ -93,7 +93,7 @@ neverallow all_untrusted_apps sysfs_net:file no_rw_file_perms;
neverallow all_untrusted_apps sysfs_type:file { no_w_file_perms no_x_file_perms };
# Apps may never access the default sysfs label.
-neverallow all_untrusted_apps sysfs:file no_rw_file_perms;
+neverallow { all_untrusted_apps -untrusted_app_27 } sysfs:file no_rw_file_perms;
--- a/system/sepolicy/prebuilts/api/30.0/private/coredomain.te
+++ b/system/sepolicy/prebuilts/api/30.0/private/coredomain.te
@@ -108,6 +108,7 @@ full_treble_only(`
-init
-ueventd
-vold
+ -untrusted_app_27
} sysfs:file no_rw_file_perms;
--- a/system/sepolicy/private/app_neverallows.te
+++ b/system/sepolicy/private/app_neverallows.te
@@ -93,7 +93,7 @@ neverallow all_untrusted_apps sysfs_net:file no_rw_file_perms;
neverallow all_untrusted_apps sysfs_type:file { no_w_file_perms no_x_file_perms };
# Apps may never access the default sysfs label.
-neverallow all_untrusted_apps sysfs:file no_rw_file_perms;
+neverallow { all_untrusted_apps -untrusted_app_27 } sysfs:file no_rw_file_perms;
--- a/system/sepolicy/private/coredomain.te
+++ b/system/sepolicy/private/coredomain.te
@@ -108,6 +108,7 @@ full_treble_only(`
-init
-ueventd
-vold
+ -untrusted_app_27
} sysfs:file no_rw_file_perms;
通过上述的这些文件对selinux权限的授权,发现在读取/sys/class/thermal/thermal_zone*/temp 的节点的时候,就能够正常读取当前的cpu和电池的温度,得到的值除以1000就是摄氏度值, 在节点的type类型中,mtktsbattery代表是电池温度,mtktscpu代表是cpu温度,所以这样就 可以轮询获取温度值就可以了