车载应用开发基础:Android Automotive 应用限制、多窗口与分屏策略、安全与权限模型

好,我们进入车载应用开发的核心环节。说实话,很多从手机端转过来的开发者,第一次接触 Automotive 都会有点懵——怎么这个不能用、那个被限制了?别急,今天我就带你把这些「坑」一个个填平。

一、Android Automotive 的应用限制

车载系统和手机系统最大的区别是什么?安全。你想想看,手机卡顿顶多被用户骂两句,车载系统卡顿或者崩溃,那可是要出大事的。所以 Google 在 Automotive 上加了很多「紧箍咒」。

1.1 哪些应用类型被限制?

我个人习惯把 Automotive 的应用分成三类:

  • 系统级应用:比如 Launcher、SystemUI、Settings,这些是 OEM 定制的核心
  • 车载核心应用:比如地图、音乐、电话,这些是驾驶场景刚需
  • 第三方应用:通过 Google Play 或其他渠道安装的普通应用

嗯,这里要注意——第三方应用的限制是最多的。我在项目中遇到过,某知名音乐 App 想在车载上做全屏沉浸式播放界面,结果发现根本跑不起来。为什么?因为 Automotive 默认禁止了非系统应用的 SYSTEM_ALERT_WINDOW 权限。

核心限制清单:

  • 禁止使用 SYSTEM_ALERT_WINDOW(悬浮窗)
  • 禁止使用 REQUEST_INSTALL_PACKAGES(静默安装)
  • 禁止使用 BIND_NOTIFICATION_LISTENER_SERVICE(通知监听)
  • 禁止使用 MANAGE_EXTERNAL_STORAGE(外部存储管理)
  • 禁止使用 REQUEST_DELETE_PACKAGES(卸载应用)

1.2 为什么要有这些限制?

说白了,就是怕应用「作妖」。你想想看,如果某个第三方应用弹了个悬浮窗,刚好挡住了导航的关键路口提示,那后果不堪设想。Google 在 PackageManager 里加了一层过滤,所有非系统应用在安装时就会被标记,运行时再检查权限。

我曾经调试过一个案例:某地图 App 在手机上能正常显示悬浮导航,移植到 Automotive 上就闪退。查了半天,原来是 AndroidManifest.xml 里声明了 SYSTEM_ALERT_WINDOW,而 Automotive 直接把这个权限给禁了。解决方案?要么改成系统应用,要么用 CarAppFocusManager 来管理窗口焦点。

二、多窗口与分屏策略

车载屏幕越来越大,12.3寸、15.6寸甚至更大的都有。但屏幕大不代表你可以随便用——驾驶场景下,信息呈现必须「克制」。

2.1 Automotive 的窗口管理架构

Android Automotive 的窗口管理,说白了就是 WindowManager 加上一层车载特有的 CarWindowManager。我画了一张图,帮你理清这个关系:

Android Automotive 窗口管理架构 应用层 地图App | 音乐App | 电话App | 第三方App CarWindowManager(车载窗口策略层) 分屏策略 | 窗口限制 | 焦点管理 | 安全区域 WindowManager(Android 原生窗口管理) 窗口创建 | 布局计算 | 输入事件分发 SurfaceFlinger(合成与显示)

你看,CarWindowManager 夹在应用和原生窗口管理器之间,它负责做「策略决策」。比如:当前是驾驶模式还是停车模式?导航窗口应该占多大面积?这些都由它说了算。

2.2 分屏策略实战

Automotive 支持两种分屏模式:

模式 说明 典型场景
并排分屏 左右各一个应用,比例可调 导航 + 音乐
画中画 小窗口悬浮在主应用之上 导航 + 视频通话

我建议你在开发时,优先考虑 并排分屏。为什么?因为画中画虽然酷,但容易遮挡关键信息。我记得有一次,某 OEM 的客户坚持要用画中画显示导航,结果在高速上小窗口把限速提示给挡住了——嗯,后来他们乖乖改成了并排分屏。

避坑指南: 我曾经在适配分屏时踩过一个坑——onMultiWindowChanged 回调在 Automotive 上触发时机和手机不一样。手机是窗口变化后立即回调,而 Automotive 会等动画完成后再回调。解决方案是监听 CarWindowManageronWindowFocusChanged 来替代。

2.3 如何让你的应用支持分屏?

其实很简单,在 AndroidManifest.xml 里声明:

<activity
    android:name=".MainActivity"
    android:resizeableActivity="true"
    android:supportsPictureInPicture="true"
    android:configChanges="screenSize|smallestScreenSize|screenLayout">
</activity>

但光声明还不够。你需要在代码里处理 onMultiWindowChanged,动态调整 UI 布局。比如导航应用在分屏模式下,应该把地图放大、把搜索栏缩小。

三、安全与权限模型

车载系统的安全模型,比手机严格得多。我甚至觉得,Google 把 Automotive 的权限模型设计得有点像「军事化管理」。

3.1 权限分级

Automotive 把权限分成四个等级:

  • 普通权限:安装时自动授予,比如 INTERNET
  • 危险权限:运行时弹窗询问,比如 ACCESS_FINE_LOCATION
  • 签名权限:只有同签名的应用才能获取,比如 BIND_CAR_SERVICE
  • 特权权限:只有系统应用才能获取,比如 CONTROL_CAR_CLUSTER

注意: 在 Automotive 上,危险权限的弹窗会被系统拦截。你想想看,如果导航到一半突然弹个权限请求框,司机还得伸手去点——这太危险了。所以 Google 规定,所有危险权限必须在应用首次启动时集中申请,或者通过设置页面静默授权。

3.2 车载特有的权限

除了 Android 原生权限,Automotive 还加了一批车载专属权限:

权限名 用途 等级
android.car.permission.CAR_INFO 读取车辆基本信息(VIN、速度等) 签名
android.car.permission.CAR_MILEAGE 读取里程数据 签名
android.car.permission.CAR_ENERGY 读取能源信息(电量、油耗) 签名
android.car.permission.CAR_CONTROL_AUDIO_VOLUME 控制音量 特权
android.car.permission.CAR_CONTROL_CLUSTER 控制仪表盘显示 特权

我建议你在开发车载应用时,尽量使用签名权限。为什么?因为特权权限需要 OEM 签名,普通第三方应用根本拿不到。而签名权限,只要你跟系统用同一个签名,就能获取。

3.3 权限申请的最佳实践

嗯,这里我要分享一个我自己的经验。很多开发者喜欢在 onCreate 里一股脑申请所有权限,这在 Automotive 上是大忌。为什么?因为用户可能正在开车,突然弹出一堆权限请求,他只能靠边停车来处理。

正确的做法是:

  1. 延迟申请:等到真正需要某个功能时,再申请对应的权限
  2. 集中申请:如果必须申请多个权限,放在同一个页面一次性处理
  3. 提供说明:在申请前,先告诉用户「为什么需要这个权限」

我曾经接手过一个项目,第三方音乐 App 在启动时一口气申请了 8 个权限,结果用户直接给了差评。后来我们改成按需申请,用户满意度提升了 30%。

3.4 安全模型的核心:隔离与沙箱

Automotive 的安全模型,说白了就是「隔离」两个字。每个应用运行在自己的沙箱里,不能访问其他应用的数据。系统服务通过 CarService 暴露接口,应用只能通过 Car 类来调用。

举个例子,你想获取当前车速:

// 错误做法:直接读取系统属性
String speed = SystemProperties.get("car.speed"); // 这行代码会报 SecurityException

// 正确做法:通过 CarService
Car car = Car.createCar(context);
CarInfoManager infoManager = (CarInfoManager) car.getCarManager(Car.CAR_INFO_SERVICE);
float speed = infoManager.getVehicleSpeed();

你看,所有对车辆硬件的访问,都必须经过 CarService。这层封装不仅保证了安全,也让 OEM 可以灵活控制哪些应用能访问哪些数据。

核心要点总结:

  • 第三方应用受限严重,系统应用才是「亲儿子」
  • 分屏优先用并排模式,画中画慎用
  • 权限申请要「按需、延迟、集中」
  • 所有车辆数据访问必须通过 CarService

好了,这一章的内容就到这里。记住,车载开发的核心思维是「安全第一,体验第二」。你设计的每一个功能,都要先问自己:这个功能在司机开车时会不会造成干扰?如果会,那就重新设计。

公众号:蓝海资料掘金营,微信deep3321