如何让Android应用获取读取内部存储CPU温度文件的权限?
Android应用读取CPU温度文件权限问题解决
问题描述
尝试读取CPU温度文件/sys/devices/virtual/thermal/thermal_zone0/temp时遇到权限拒绝错误,日志如下:
2023-03-03 23:21:12.178 5266-5266 System.err com.example.racestats W java.io.FileNotFoundException: /sys/devices/virtual/thermal/thermal_zone0/temp: open failed: EACCES (Permission denied) 2023-03-03 23:21:12.179 5266-5266 System.err com.example.racestats W at libcore.io.IoBridge.open(IoBridge.java:574) 2023-03-03 23:21:12.179 5266-5266 System.err com.example.racestats W at java.io.RandomAccessFile.<init>(RandomAccessFile.java:289) 2023-03-03 23:21:12.179 5266-5266 System.err com.example.racestats W at java.io.RandomAccessFile.<init>(RandomAccessFile.java:152) 2023-03-03 23:21:12.179 5266-5266 System.err com.example.racestats W at com.example.racestats.MainActivityKt.getCpuTemperature(MainActivity.kt:171) 2023-03-03 23:21:12.180 5266-5266 System.err com.example.racestats W at com.example.racestats.MainActivityKt.cpuTemperature$lambda$0(MainActivity.kt:157) 2023-03-03 23:21:12.180 5266-5266 System.err com.example.racestats W at com.example.racestats.MainActivityKt.$r8$lambda$vjQE1RADCplWY99vEEfe1Z4FocA(Unknown Source:0) 2023-03-03 23:21:12.180 5266-5266 System.err com.example.racestats W at com.example.racestats.MainActivityKt$$ExternalSyntheticLambda0.onClick(Unknown Source:4) 2023-03-03 23:21:12.180 5266-5266 System.err com.example.racestats W at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:175) 2023-03-03 23:21:12.180 5266-5266 System.err com.example.racestats W at android.os.Handler.dispatchMessage(Handler.java:106) 2023-03-03 23:21:12.180 5266-5266 System.err com.example.racestats W at android.os.Looper.loopOnce(Looper.java:201) 2023-03-03 23:21:12.180 5266-5266 System.err com.example.racestats W at android.os.Looper.loop(Looper.java:288) 2023-03-03 23:21:12.180 5266-5266 System.err com.example.racestats W at android.app.ActivityThread.main(ActivityThread.java:7872) 2023-03-03 23:21:12.180 5266-5266 System.err com.example.racestats W at java.lang.reflect.Method.invoke(Native Method) 2023-03-03 23:21:12.180 5266-5266 System.err com.example.racestats W at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) 2023-03-03 23:21:12.180 5266-5266 System.err com.example.racestats W at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) 2023-03-03 23:21:12.181 5266-5266 System.err com.example.racestats W Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied) 2023-03-03 23:21:12.181 5266-5266 System.err com.example.racestats W at libcore.io.Linux.open(Native Method) 2023-03-03 23:21:12.181 5266-5266 System.err com.example.racestats W at libcore.io.ForwardingOs.open(ForwardingOs.java:563) 2023-03-03 23:21:12.181 5266-5266 System.err com.example.racestats W at libcore.io.BlockGuardOs.open(BlockGuardOs.java:274) 2023-03-03 23:21:12.181 5266-5266 System.err com.example.racestats W at libcore.io.ForwardingOs.open(ForwardingOs.java:563) 2023-03-03 23:21:12.181 5266-5266 System.err com.example.racestats W at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:7758) 2023-03-03 23:21:12.181 5266-5266 System.err com.example.racestats W at libcore.io.IoBridge.open(IoBridge.java:560) 2023-03-03 23:21:12.181 5266-5266 System.err com.example.racestats W ... 14 more
当前Kotlin代码
/** * This function will set the CPU Temp in are UI and will handles all things associated with the CPU temp. */ fun cpuTemperature(activity: Activity, callback: (Float) -> Unit) { val permissionCheck = ContextCompat.checkSelfPermission(activity, Manifest.permission.MANAGE_EXTERNAL_STORAGE) if (permissionCheck != PackageManager.PERMISSION_GRANTED) { if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.MANAGE_EXTERNAL_STORAGE)) { Toast.makeText(activity, "This permission is required to access the CPU temperature.", Toast.LENGTH_LONG).show() } val alertDialogBuilder = AlertDialog.Builder(activity) alertDialogBuilder.setTitle("Permission Request") alertDialogBuilder.setMessage("The app needs access to external storage to read the CPU temperature. Do you allow this permission?") alertDialogBuilder.setPositiveButton("Allow") { _, _ -> ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.READ_INTERNAL_STORAGE), 1) ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), 1) getCpuTemperature(callback) } alertDialogBuilder.setNegativeButton("Deny") { dialog, _ -> dialog.dismiss() // set cpu temp to be invisible } alertDialogBuilder.show() } else { getCpuTemperature(callback) } } fun getCpuTemperature(callback: (Float) -> Unit) { return try { val reader = RandomAccessFile("/sys/devices/virtual/thermal/thermal_zone0/temp", "r") val line: String = reader.readLine() if (line != null) { val temp = line.toFloat() println(temp / 1000.0f) callback(temp / 1000.0f) } else { println(51.0f) callback(51.0f) } } catch (e: Exception) { println("about to look for temp") e.printStackTrace() println("Temp is below") println(0.0f) callback(0.0f) } }
当前AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" /> <application android:allowBackup="true" android:requestLegacyExternalStorage="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.RaceStats" tools:targetApi="31"> <activity android:name=".TopSpeed" android:exported="false"> <meta-data android:name="android.app.lib_name" android:value="" /> </activity> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <meta-data android:name="android.app.lib_name" android:value="" /> </activity> </application> </manifest>
核心问题分析
权限方向错误:你申请的
READ_EXTERNAL_STORAGE、MANAGE_EXTERNAL_STORAGE是针对外部存储(SD卡、公共存储目录)的权限,而/sys/devices/virtual/thermal属于系统虚拟文件系统,和外部存储完全无关,所以这些权限对读取该文件没有任何作用。另外,READ_INTERNAL_STORAGE并非Android官方定义的权限,添加它也没有意义。系统权限限制:非Root的普通Android设备中,
/sys下的大部分文件仅允许root用户或system组进程读取,第三方APP的UID不在允许范围内,直接读取会触发EACCES权限拒绝。
可行解决方案
方案一:使用官方ThermalManager API(推荐,API 31+)
Android 12(API 31)及以上提供了ThermalManager系统服务,可以安全获取设备温度数据,无需直接访问/sys文件:
修正后的Kotlin代码
fun getCpuTemperature(activity: Activity, callback: (Float) -> Unit) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val thermalManager = activity.getSystemService(Context.THERMAL_SERVICE) as ThermalManager val thermalZones = thermalManager.thermalZones // 遍历热区,找到CPU相关的温度 for (zone in thermalZones) { if (zone.type.contains("cpu", ignoreCase = true) || zone.name.contains("cpu", ignoreCase = true)) { val tempCelsius = zone.temperature / 1000.0f callback(tempCelsius) return } } // 未找到CPU热区时返回默认值 callback(0.0f) } else { // 处理低版本系统,尝试兼容方式 handleLegacyCpuTemp(callback) } } // 低版本系统兼容方法(部分设备可能可用) private fun handleLegacyCpuTemp(callback: (Float) -> Unit) { val possiblePaths = arrayOf( "/sys/devices/virtual/thermal/thermal_zone0/temp", "/sys/class/thermal/thermal_zone0/temp", "/sys/devices/platform/soc/soc:qcom,mdss_fb.19200000.mdss_fb/thermal/temp" ) for (path in possiblePaths) { try { val reader = RandomAccessFile(path, "r") val line = reader.readLine()?.trim() reader.close() if (!line.isNullOrEmpty()) { val temp = line.toFloat() callback(temp / 1000.0f) return } } catch (e: Exception) { // 路径不可读,尝试下一个 continue } } // 所有路径都失败 callback(0.0f) }
修正后的Manifest
移除无用权限,仅保留必要配置:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.RaceStats" tools:targetApi="31"> <activity android:name=".TopSpeed" android:exported="false"> <meta-data android:name="android.app.lib_name" android:value="" /> </activity> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <meta-data android:name="android.app.lib_name" android:value="" /> </activity> </application> </manifest>
方案二:Root设备(仅测试用)
如果你的应用仅面向Root用户,可以在获取Root权限后执行命令读取文件:
fun getRootCpuTemperature(callback: (Float) -> Unit) { try { val process = Runtime.getRuntime().exec("su -c cat /sys/devices/virtual/thermal/thermal_zone0/temp") val reader = BufferedReader(InputStreamReader(process.inputStream)) val line = reader.readLine()?.trim() reader.close() process.waitFor() if (!line.isNullOrEmpty()) { val temp = line.toFloat() callback(temp / 1000.0f) return } } catch (e: Exception) { e.printStackTrace() } callback(0.0f) }
注意:这种方式无法上架Google Play,且仅适用于Root设备。
方案三:依赖厂商适配
部分厂商会开放部分热区文件的读取权限,你可以尝试遍历多个可能的热区路径(如thermal_zone1、thermal_zone2等),但这种方式兼容性差,无法保证所有设备可用。
关键注意事项
- 不要申请无关权限,避免用户困惑和Play Store审核问题。
- 官方API是唯一稳定的获取温度方式,优先使用
ThermalManager。 - 低版本系统的兼容方案无法保证所有设备可用,需做好异常处理。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

