21、安全与权限:AAOS 安全模型、系统签名、SELinux 策略、权限管理
说实话,做车机系统开发,安全这块是绕不过去的坎。我刚开始接触 AAOS 时,觉得不就是个 Android 吗?后来才发现,车上的安全要求比手机严格得多。你想想看,手机死机了重启就行,车机要是被攻破了,那可是人命关天的事。
这一章,我就带你捋一遍 AAOS 的安全体系。说白了,就是搞清楚三件事:谁可以做什么、谁可以访问什么、出了问题怎么追责。
21.1 AAOS 安全模型:分层隔离
AAOS 的安全模型,核心思想就是隔离。我习惯把它想象成一栋楼——不同楼层住着不同的人,楼层之间装了铁门,没钥匙进不去。
AAOS 安全分层(从底层到上层):
- 硬件层:TrustZone、硬件密钥、安全存储
- 内核层:Linux 内核安全、SELinux 强制访问控制
- 框架层:Android 权限模型、系统签名
- 应用层:应用沙箱、进程隔离
每一层都依赖下一层提供安全保障。比如,应用层的权限检查,最终要依赖内核层的 SELinux 策略来强制执行。我在项目中遇到过一个问题:明明应用声明了权限,但运行时还是被拒绝。查了半天,发现是 SELinux 策略没放行——这就是分层隔离的典型体现。
21.2 系统签名:车机的身份证
系统签名,说白了就是给应用发一张「身份证」。只有持有正确签名的应用,才能被系统信任。
AAOS 里有两类签名:
| 签名类型 | 用途 | 谁可以使用 |
|---|---|---|
| 平台签名(platform) | 系统级应用、系统服务 | OEM 厂商 |
| 媒体签名(media) | 多媒体相关应用 | OEM 或授权第三方 |
| 共享签名(shared) | 同一开发者的多个应用 | 应用开发者 |
| 测试签名(testkey) | 开发调试阶段 | 开发者 |
注意:千万不要把测试签名用在量产车上!我曾经见过一个团队,因为赶工期,直接用 testkey 签了系统应用。结果车上市后,第三方应用可以轻易伪造系统签名——那场面,简直是灾难。
生成系统签名的命令,我一般这么用:
# 生成平台签名密钥
openssl genrsa -out platform.pem 2048
openssl req -new -x509 -key platform.pem -out platform.x509.pem -days 10000
# 使用 signapk.jar 签名
java -jar signapk.jar platform.x509.pem platform.pem \
input.apk output.apk
嗯,这里要注意:密钥文件一定要妥善保管。我习惯把它们放在硬件安全模块(HSM)里,而不是直接放在构建服务器上。你想想看,密钥泄露了,整个车机系统就相当于裸奔了。
21.3 SELinux 策略:最后的防线
SELinux,全称是 Security-Enhanced Linux。说白了,它就是内核里的一把锁——即使应用拿到了 root 权限,SELinux 照样能拦住它。
AAOS 里 SELinux 的策略文件,通常放在 system/sepolicy/ 目录下。我刚开始看这些文件时,头都大了——全是 .te 后缀的文件,语法还很奇怪。
举个例子,假设我们要给一个车载导航应用放行 GPS 权限:
# 文件:navigation_app.te
# 定义类型
type navigation_app, domain;
type navigation_app_exec, exec_type, file_type;
# 初始化规则
init_daemon_domain(navigation_app)
# 允许访问 GPS 设备
allow navigation_app gps_device:chr_file rw_file_perms;
# 允许读取位置服务
allow navigation_app location_service:service_manager find;
这里的关键是 allow 语句。它的格式是:
allow 源类型 目标类型:类别 权限集合;
我个人的习惯是:先写一个宽松的策略,然后逐步收紧。因为一开始你根本不知道应用需要哪些权限。我曾经有一次,把策略写得太严了,结果导航应用死活拿不到 GPS 数据。排查了一整天,最后发现是少了一条 allow 规则。
调试技巧:使用 adb shell dmesg | grep avc 查看 SELinux 拒绝日志。如果看到类似 avc: denied 的信息,那就是策略没放行。
21.4 权限管理:谁可以做什么
Android 的权限模型,在 AAOS 里被进一步强化了。车机上的权限,不光要管「能不能用」,还要管「什么时候能用」。
举个例子,一个音乐应用想访问麦克风。在手机上,用户同意就行。但在车上,你还要考虑:
- 车辆是否在行驶中?
- 驾驶员是否在通话?
- 是否有紧急事件(如碰撞预警)正在播报?
AAOS 引入了一个叫 CarPermissionManager 的东西,专门处理这类场景。我写过一个示例:
// 检查是否有权限使用麦克风
CarPermissionManager carPermissionManager =
Car.createCar(this).getCarManager(CarPermissionManager.class);
if (carPermissionManager != null) {
int result = carPermissionManager.checkPermission(
"android.car.permission.CAR_MICROPHONE",
getPackageName()
);
if (result == PackageManager.PERMISSION_GRANTED) {
// 可以访问麦克风
startRecording();
} else {
// 权限被拒绝,可能是车辆状态不允许
showPermissionDeniedDialog();
}
}
这里有个坑:不要假设权限一旦授予就永远有效。车机的权限状态可能会因为车辆状态变化而动态调整。比如,车辆从「驻车」切换到「行驶」时,某些权限会被自动撤销。
21.5 实战:配置一个安全的车机应用
好了,理论说完了,咱们来点实际的。假设我们要开发一个车载诊断应用,需要访问车辆的 OBD 数据。安全配置应该怎么做?
第一步,在 AndroidManifest.xml 中声明权限:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.obdapp">
<!-- 系统级权限,需要平台签名 -->
<uses-permission android:name="android.car.permission.CAR_DIAGNOSTIC" />
<application
android:sharedUserId="android.uid.system"
...>
...
</application>
</manifest>
第二步,编写 SELinux 策略:
# obd_app.te
type obd_app, domain;
type obd_app_exec, exec_type, file_type;
init_daemon_domain(obd_app)
# 允许访问诊断服务
allow obd_app car_diagnostic_service:service_manager find;
allow obd_app car_diagnostic_service:binder call;
# 允许读取车辆网络数据
allow obd_app vehicle_network_device:chr_file rw_file_perms;
第三步,使用平台签名对应用进行签名:
java -jar signapk.jar platform.x509.pem platform.pem \
obd_app_unsigned.apk obd_app_signed.apk
避坑指南:我曾经犯过一个错误——在开发阶段用了 testkey 签名,然后直接刷到测试车上。结果 SELinux 策略死活不生效,因为 testkey 签名的应用被系统当作「第三方应用」处理,根本不会加载对应的策略文件。记住:测试也要用 platform 签名,除非你只是想验证功能逻辑。
21.6 安全审计:出了问题怎么查
安全配置做得再好,也难免出问题。关键是出了问题能快速定位。
我常用的审计手段:
- 查看 logcat:搜索
Permission、SecurityException等关键词 - 查看 dmesg:搜索
avc: denied定位 SELinux 拒绝 - 使用
adb shell dumpsys:查看权限授予状态 - 检查签名:用
jarsigner -verify -verbose -certs app.apk确认签名是否正确
举个例子,有一次我发现某个系统应用无法启动,logcat 里报 SecurityException。我第一反应是权限问题,但检查了 AndroidManifest 发现权限声明没问题。后来用 dmesg | grep avc 一看,原来是 SELinux 策略里少了一条 allow 规则。加上之后,问题就解决了。
嗯,安全这东西,说白了就是「防患于未然」。配置的时候多花点心思,出问题的时候就能少掉点头发。