笔者在从事Android车载行业的开发过程中,发现Android车载开发和平时的Android开发还是有很大不同之处,对于一个小白来说或者说如果是刚入行的新人都会很陌生,目前市场也没有很多系统性的知识提供给大家。
所以笔者准备通过一个专栏系列,把自己在车载开发过程中的学习记录和开发经验记录下来并分享出来,希望能给大家带来一些帮助。
在第一篇内容,笔者介绍了Android车载操作系统现状、整个操作系统架构和架构下核心概念:
本篇内容会落到和实际应用开发直接相关的CarFramework
,CarFramework
在Android Automotive
操作系统中扮演着类似于Android
框架在智能手机上的角色。
它是Android Automotive
操作系统中的一个关键组件,提供了与车辆系统交互的基础设施和功能。CarFramework
为车载应用程序提供统一的开发和执行环境,以便它们可以与车辆的硬件和软件进行交互。
主要内容如下:
- Android Automotive、Android 和 Android Auto的关系和区别
- Android Automotive的架构
- CarService的启动流程
- CarAPI、CarAPP
Android Automotive
看看Google
是怎么定义Android Automotive
的
Android Automotive is a full-stack, open source, highly customizable platform running directly on in-vehicle hardware.
Android Automotive
是一个能直接在车载硬件上运行的全栈、开源、高度可定制的平台。
所以很重要的一点, Android Automotive
是一个基础平台。
这个平台可以运行预装在 IVI (in-vehicle infotainment)
系统的 Android
应用程序以及三方Android
应用程序。
由于是开源的,可以通过在免费开源代码库中提供基本的汽车信息娱乐功能来提高效率。它的开放性,使开发者可以根据根据需求来定制化产品。
既然是一个平台,那和 Android
平台有什么区别,是在Android
之外的一个分叉开发吗?Android Automotive
与整个Android
生态系统是个什么关系?
Android Automotive & Android
Android Automotive
本质上还是 Android
。
Android Automotive
不是 Android
的分叉或并行开发。它与手机、平板电脑等设备上的 Android
具有相同的代码库和存储库。它构建在经过 10 多年开发的强大平台和功能集之上,使其能够利用现有的安全模型、兼容性程序、开发人员工具和基础设施,同时继续高度可定制和可移植,完全免费和开源。
Android Automotive
是对Android
进行了扩展。在将Android
构建为功能齐全的信息娱乐平台的过程中,添加了对汽车特定要求、功能和技术的支持。
Android Automotive & Android Auto
Android Automotive
和 Android Auto
这两个词放在一起就容易晕,混淆不清。其实它们的区别还是很大的:
Android Automotive
上面说过,它本质上是Android
平台,是需要在车载硬件上运行的。
而 Android Auto
是一个在用户手机上运行的平台,它的作用是:
通过 USB / 无线
连接将手机上的部分应用投射到兼容的车载信息娱乐系统。Android Auto
支持专为车载使用而设计的应用程序。它和苹果手机上的CarPlay
属于同类平台。
Android Automotive架构
先看下Google
给出了车载HAL与Android Automotive
架构:
- Car APP: 上层应用,包含系统APP、OEM APP、三方的App
- Car API:提供给上层应用的功能API,比如多媒体、仪表盘、空调、导航等。内有包含
CarSensorManager
在内的API。代码包路径(/platform/packages/services/Car/car-lib) - CarService:系统中与车相关的一系列服务,主要是基于
CarProperty
实现Vechile
相关的一些策略。代码包路径(/platform/packages/services/Car/) - VehicleHAL:汽车的硬件抽象层描述。用于定义 OEM 可以实现的车辆属性的接口。包含属性元数据。例如,车辆属性是否为 int 以及允许使用哪些更改模式。代码包路径(hardware/interfaces/automotive/vehicle/2.0/default/)
车载HAL
与Android Automotive
架构可以这么理解:
Android
为Automotive
场景提供了一系列的服务,这些服务统被称为CarService
。它们是衔接上下层的桥梁。
往下层看:CarService
与HAL
层的VehicleHAL
通信,进而通过车载总线(例如CAN总线)与车身进行通讯;
往上层看:CarService
为应用层的APP
提供接口Car API
,从而让APP
能够实现对车身的控制与状态的显示。
所以,接下来介绍下这个架构中的“显眼包” - CarService
CarService
目录结构
原生的CarService
业务代码庞大,包含了许多与汽车相关的服务,它的一级目录代码路径位于:
/platform/packages/services/Car/
目录结构是这样的:
.
├── Android.mk
├── apicheck.mk
├── apicheck_msg_current.txt
├── apicheck_msg_last.txt
├── car-cluster-logging-renderer //LoggingClusterRenderingService继承InstrumentClusterRenderingService
├── car-default-input-service //按键消息处理
├── car-lib //提供给汽车App特有的接口,许多定制的模块都在这里实现,包括Sensor,HVAC,Cabin,ActiveParkingAssiance,Diagnostic,Vendor等
├── car-maps-placeholder //地图软件相关
├── car_product //系统编译相关
├── car-support-lib //android.support.car
├── car-systemtest-lib //系统测试相关
├── car-usb-handler //开机自启,用于管理车机USB
├── CleanSpec.mk
├── evs
├── obd2-lib
├── PREUPLOAD.cfg
├── procfs-inspector
├── service //com.android.car是一个后台运行的组件,可以长时间运行并且不需要和用户去交互的,这里即使应用被销毁,它也可以正常工作
├── tests
├── tools //是一系列的工具,要提到的是里面的emulator,测试需要用到的。python写的,通过adb可以连接vehicleHal的工具,用于模拟测试
├── TrustAgent
└── vehicle-hal-support-lib
启动流程
CarService
是Android中的服务组件,所以必须要了解下CarService
的启动流程:
1.启动CarServiceHelperService
系统服务SystemServer
启动时,会启动CarServiceHelperService
服务:
SystemServe: main() -> run() —-> startOtherServices()
// frameworks/base/services/java/com/android/server/SystemServer.java
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
...
startBootstrapServices(t);
startCoreServices(t);
startOtherServices(t); //⭐️
startApexServices(t);
...
}
// 启动CarServiceHelperService服务
private static final String CAR_SERVICE_HELPER_SERVICE_CLASS =
"com.android.internal.car.CarServiceHelperService";
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
boolean isAutomotive = mPackageManage
.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
if (isAutomotive) {
final SystemService cshs = mSystemServiceManager
.startService(CAR_SERVICE_HELPER_SERVICE_CLASS);
}
}
2.启动CarService
CarServiceHelperService
的onStart
方法最终会通过bindService
的方式启动CarService
-> CarServiceHelperService.java: onStart() -> CarServiceHelperServiceUpdatableImpl: onStart() -> bindService() -> CarService.java: onCreate() -> CarServiceImpl.java: onCreate()
CarServiceHelperService#onStart()
启动CarServiceHelperService
服务时,最终会调用到CarServiceHelperService
的onStart
方法里
onStart
方法最终会通过绑定服务的方式启动服务,这个服务就是CarService
其中绑定service
对应的包是:"com.android.car",对应的action
是:"android.car.ICar"
//frameworks/opt/car/services/builtInServices/src/com/android/internal/car/CarServiceHelperService.java
public class CarServiceHelperService extends SystemService
implements Dumpable, DevicePolicySafetyChecker, CarServiceHelperInterface {
...
mCarServiceHelperServiceUpdatable.onStart();
}
public static final String CAR_SERVICE_INTERFACE = "android.car.ICar";
private static final String CAR_SERVICE_PACKAGE = "com.android.car";
public final class CarServiceHelperServiceUpdatableImpl
implements CarServiceHelperServiceUpdatable, Executor {
@Override
public void onStart() {
// 设置action(CAR_SERVICE_INTERFACE)和package(CAR_SERVICE_PACKAGE)
Intent intent = new Intent(CAR_SERVICE_INTERFACE).setPackage(CAR_SERVICE_PACKAGE);
Context userContext = mContext.createContextAsUser(UserHandle.SYSTEM, /* flags= */ 0);
if (!userContext.bindService(intent, Context.BIND_AUTO_CREATE, this,
mCarServiceConnection)) {
Slogf.wtf(TAG, "cannot start car service");
}
}
}
bindService()
通过bindService
方式来启动Service
会经过如下阶段:
context.bindService() ——> onCreate() ——> onBind() ——> Service running
bindService
后,最终会通过CarServiceImpl
去创建ICarImpl
对象
/** Proxy service for CarServciceImpl */
public class CarService extends ServiceProxy {
private static final int MAX_BINDER_THREADS = 31;
public static final String CAR_SERVICE_IMPL_CLASS = "com.android.car.CarServiceImpl";
public CarService() {
super(UpdatablePackageDependency.CAR_SERVICE_IMPL_CLASS);
// Increase the number of binder threads in car service
BinderInternal.setMaxThreads(MAX_BINDER_THREADS);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// keep it alive.
return START_STICKY;
}
}
public class ServiceProxy extends Service {
private final String mRealServiceClassName;
private UpdatablePackageContext mUpdatablePackageContext;
private Class mRealServiceClass;
private ProxiedService mRealService;
public ServiceProxy(String realServiceClassName) {
mRealServiceClassName = realServiceClassName;
}
@Override
public void onCreate() {
init(); // ⭐️
mRealService.onCreate();
}
// ⭐️
private void init() {
mUpdatablePackageContext = UpdatablePackageContext.create(this);
try {
mRealServiceClass = mUpdatablePackageContext.getClassLoader().loadClass(
mRealServiceClassName);
Constructor constructor = mRealServiceClass.getConstructor();
mRealService = (ProxiedService) constructor.newInstance();
mRealService.doAttachBaseContext(mUpdatablePackageContext);
mRealService.setBuiltinPackageContext(this);
} catch (Exception e) {
throw new RuntimeException("Cannot load class:" + mRealServiceClassName, e);
}
}
}
public class CarServiceImpl extends ProxiedService {
@Override
public void onCreate() {
...
mICarImpl = new ICarImpl(this,
getBuiltinPackageContext(),
mVehicle,
SystemInterface.Builder.defaultSystemInterface(this).build(),
mVehicleInterfaceName);
mICarImpl.init();
...
super.onCreate();
...
}
}
ICarImpl
的构造函数会添加很多核心服务到集合列表中,然后在init
方法中去初始化这些的服务
void ICarImpl(Context serviceContext, ...){
List<CarServiceBase> allServices = new ArrayList<>();
allServices.add(mSystemActivityMonitoringService);
allServices.add(mCarPowerManagementService);
allServices.add(mCarPropertyService);
allServices.add(mCarDrivingStateService);
allServices.add(mCarUXRestrictionsService);
....
mAllServices = allServices.toArray(new CarSystemService[allServices.size()]);
}
void init() {
if (!mDoPriorityInitInConstruction) {
priorityInit();
}
for (CarSystemService service : mAllServices) {
t.traceBegin(service.getClass().getSimpleName());
service.init(); // ⭐️
t.traceEnd();
}
mCarOemService.onInitComplete();
}
3.建立双向通信
CarServiceImpl
类中的onBind
会将该ICarImpl
对象返回给CarServiceHelperService
public class CarServiceImpl extends ProxiedService {
@Override
public IBinder onBind(Intent intent) {
return mICarImpl;
}
}
mICarImpl
会作为IBinder
返回给CarServiceHelperService
类的bindServiceAsUser
方法中的参数mCarServiceConnection
,此时CarServiceHelperService
就拿到了CarService
的binder
对象,可以调用接口方法,进行通信了。
#frameworks/opt/car/services/updatableServices/src/com/android/internal/car/updatable/CarServiceHelperServiceUpdatableImpl.java
final ICarServiceHelperImpl mHelper = new ICarServiceHelperImpl();
private final ServiceConnection mCarServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
handleCarServiceConnection(iBinder); // ⭐️
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
handleCarServiceCrash();
}
};
void handleCarServiceConnection(IBinder iBinder) {
synchronized (mLock) {
mCarServiceBinder = ICar.Stub.asInterface(iBinder); // ⭐️
}
...
sendSetSystemServerConnectionsCall(); // ⭐️
}
CarServiceHelperService
拿到该binder
对象后的第一件事,就把自己的binder
句柄mHelper
,通过setSystemServerConnections
方法发送给了CarService
。
private void sendSetSystemServerConnectionsCall() {
ICar binder;
synchronized (mLock) {
binder = mCarServiceBinder;
}
...
binder.setSystemServerConnections(mHelper, mCarServiceConnectedCallback); // ⭐️
...
}
# packages/services/Car/service/src/com/android/car/ICarImpl.java
public final class CarServiceHelperWrapper {
public void setSystemServerConnections(ICarServiceHelper carServiceHelper,
ICarResultReceiver resultReceiver) {
...
mCarServiceHelperWrapper.setCarServiceHelper(carServiceHelper);
...
}
}
此时,CarService
和CarServiceHelperService
就成功建立起了双向跨进程通信。
最后小结下整个过程:
1.SystemServer
启动CarServiceHelperService
服务,在调用startService
后,CarServiceHelperService
的onStart
方法通过bindService的方式启动CarService 2.启动
CarService后首先调用
onCreate,创建
ICarImpl对象并初始化,在此时创建了一系列
car相关的核心服务,遍历并通过
init进行初始化 3.然后在调用
CarService的
onBind将该
ICarImpl对象返回给
CarServiceHelperService,然后
CarServiceHelperService在内部的一个
Binder对象
ICarServiceHelperImpl传递给
CarService`,建立双向跨进程。
service源码路径:packages/services/Car/service
Car API
前文提到过Car API在架构中的位置和作用::
CarService
为应用层的APP
提供接口Car API
,从而让APP
能够实现对车身的控制与状态的显示。
该源码目录包含汽车服务 API: 这些API 还作为
Android SDK
的一部分发布到最终的Android Automotive OS SDK
。所有供应商或应用程序代码可以使用此处定义的 API。 源码位置
由于Car API 仅用于开发汽车,所以并没有包含在Framework SDK中,如果需要使用,需要引入"android.car"
的java库。
类图
下面是它的类图:
- menu:车辆应用菜单相关API。
- cluster:仪表盘相关API。
- render:渲染相关API。
- pm:应用包相关API。
- diagnostic:包含与汽车诊断相关的API。
- hardware:车辆硬件相关API。
- cabin:座舱相关API。
- hvac:通风空调相关API。
- property:属性相关API(实现定制的property)。
- radio:收音机相关API。
- input:输入相关API。
- media:多媒体相关API。
- navigation:导航相关API
- settings:设置相关API
- vms:汽车监测相关API
关于Car API的使用,参考官方的的API文档就行了:Car API文档
CarApp
Car App
层是与应用开发直接相关的,通过调用Car API
,实现对车身的控制与状态的显示。比如空调、导航、多媒体功能。
判断车载功能支持
APP层在调用Car API之前首先会判断该平台是否支持车载功能
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
xxx
}
创建Car实例
通过createCar
方法可以新建一个Car
实例:
API 29之前可以使用serviceConnectionListener来创建Car对象
public static Car createCar (Context context,
ServiceConnection serviceConnectionListener)
但这个方法在API 29就过时了,之后可以使用 CarServiceLifecycleListener
创建新的 Car 对象。 在释放传递的 {code Context} 之前,应通过调用disconnect() 来断开用此创建的实例与汽车服务的连接。
public static Car createCar (Context context,
Handler handler,
long waitTimeoutMs,
Car.CarServiceLifecycleListener statusChangeListener)
此调用最多可阻塞指定的 waitTimeoutMs
,以等待汽车服务准备就绪。 如果汽车服务在给定时间内没有准备好,它将返回一个处于断开状态的 Car
实例。 永远阻塞主线程可能会导致系统出现 ANR(应用程序无响应)终止,如果应用程序应该在汽车服务崩溃/重新启动时继续存在,则不应使用。 如果汽车服务尚未准备好,应用程序无法执行任何操作,它仍然很有用。 在任何等待过程中,如果线程被中断,它将立即返回。
建立连接
如果是通过API 29之前ServiceConnection
的方式来创建的Car对象,就需要通过connect
方法连接CarService
public void connect ()
如果是通过createCar(android.content.Context, android.os.Handler)
的方式,就不需要调用了,也就是说这个API 在29就过时了。
获取Manager
当成功连接时可以通过getCarManager
方法获取获取汽车特定服务,拿到一个一个相关的manager
public Object getCarManager (String serviceName)
例如要获取传感器服务,就可以通过服务名Car.SENSOR_SERVICE
获取相应的Manager
去操作了:
SensorManagerServicesensorManagerService = car.getCarManager(Car.SENSOR_SERVICE);
CarService具体业务
CarService
业务量非常大,包含了许多与汽车相关的服务,由于使用比较简单,笔者不打算详细介绍。
但笔者找到介绍一些常用汽车服务的文章,写的还是比较详细的,想了解使用细节的可以参考:
CarPropertyService
绝大部分与车辆硬件功能相关联的属性,如空调,、车舱功能、车辆传感器等都是通过CarPropertyService
来读取或者设置的,具体如何使用的,可以参考这两篇博客,写的比较详细:
Android汽车服务篇(二) CarPropertyService上篇
Android汽车服务篇(三) CarPropertyService下篇
CarAudioService
在车载上,音频设备的数量还是使用场景都和手机有很大的不同,仅靠Android原有的音频服务是无法满足在车内的使用需求的。
因此AAOS对Android原有的音频机制进行了扩充。在CarService
中加入了CarAudioService
。对音频设备进行更加细致的管理,以满足车上的使用场景。
Android汽车服务篇(四) CarAudioService
CarDrivingStateService
在UX Restrictions中需要根据当前的车辆行驶状态,决定当前的限制规则. 其中行驶状态地获取就是通过CarDrivingStateService
来实现的, 它的主要职责就是对外提供车辆的行驶状态信息。应用可以使用CarDrivingStateManager获取和监听驾车状态。
Android汽车服务篇(五) CarDrivingStateService
CarPackageManagerService
主要用于车上使用场景扩充了一些包管理相关的接口. 包括黑白名单的机制.。这主要是出于安全的考虑, 车上的应用有更严格的限制, 结合用户体验限制对运行在AAOS上的应用有一个更好的约束。
Android汽车服务篇(六) CarPackageManagerService
CarPowerManagementService
汽车电源管理服务。电源管理是AAOS上又一个比较特殊的部分。由于车辆的使用场景的特殊性和复杂性, 同时需要和其他ECU(Electronic Control Unit)电子控制单元的配合,都增加了车载系统电源管理的难度。
Android汽车服务篇(七) CarPowerManagementService
概念解释
- IVI system:in-vehicle infotainment system,车载信息娱乐系统
车载信息娱乐是采用车载专用中央处理器,基于车身总线系统和互联网服务,形成的车载综合信息处理系统。IVI能够实现包括三维导航、实时路况、IPTV、辅助驾驶、故障检测、车辆信息、车身控制、移动办公、无线通讯、基于在线的娱乐功能及TSP服务等一系列应用。
- ECU:Electronic Control Unit,电子控制单元
主要作用:提供信号的输入/输出接口,接收信号、处理信号、输出信号
- HAL:Hardware Abstraction Layer,硬件抽象层
使用硬件抽象,而不是直接与硬件设备通信的程序,它将程序传达给操作系统该设备应执行的操作,然后,操作系统会向该设备生成硬件相关的指令。这意味着程序员不需要知道特定设备的工作方式,就能使他们的程序与设备兼容。
HAL的意义有以下两个方面:
-
HAL层屏蔽掉不同硬件设备的差异,为
Android
提供了统一的设备访问接口。不同的硬件厂商遵循HAL标准来实现自己的硬件控制逻辑,开发者不必关心硬件设备的差异,只需按照HAL提供的标准接口对硬件进行访问即可。 -
HAL
层帮助硬件厂商隐藏了设备的核心细节,HAL
层位于用户空间,遵循Apache
协议,允许硬件厂商不公开源码,将设备相关的实现放在Android
系统中HAL具有两种实现方式:Legacy
以及Stub HAL
,初期使用的是Legacy HAL的方式,该方式为标准的Linux共享库,其它应用程序直接调用HAL层共享库导出的函数。Google后来提出了Stub HAL的方式,仍然以共享库(.so)的形式提供,它把所有供外部访问的的方法(函数)的入口指针保存在统一的数据结构,其它程序需要访问HAL中方法时,需要先获得Stub,然后通过具体的函数指针去读写底层设备。
参考
博客参考如下文章,想深入了解具体知识点的也可以自取参考学习:
Android CarFrameWork
Android Automotive之Car API
HAL抽象层的原理