前言
上一篇文章中从整体角度描述了 Android Automotive 模块。本篇文章将对 Android Automotive 中的 APP 以及 API 部分展开描述。
上一篇:AndroidAutomotive模块介绍(一)整体介绍
下一篇:AndroidAutomotive模块介绍(三)CarService服务
正文
1、CarServiceApp
Android Automotive 为系统定制了一些专门适用车载系统的应用,以代替传统的手机应用模块。
1.1 代码路径
代码路径为:/android/packages/apps/Car
1.2 应用
代码路径应用如下所示:
ubuntu16-017:/android/packages/apps/Car$ ls -l
total 76
drwxrwxr-x 6 domain users 4096 Nov 9 2022 Cluster
drwxrwxr-x 6 domain users 4096 Nov 9 2022 Dialer
drwxrwxr-x 3 domain users 4096 Jun 7 2022 externallibs
drwxrwxr-x 5 domain users 4096 Nov 9 2022 Hvac
drwxrwxr-x 5 domain users 4096 Nov 9 2022 LatinIME
drwxrwxr-x 5 domain users 4096 Nov 9 2022 Launcher
drwxrwxr-x 5 domain users 4096 Nov 9 2022 LensPicker
drwxrwxr-x 18 domain users 4096 Nov 9 2022 libs
drwxrwxr-x 5 domain users 4096 Nov 9 2022 LinkViewer
drwxrwxr-x 6 domain users 4096 Nov 9 2022 LocalMediaPlayer
drwxrwxr-x 5 domain users 4096 Nov 9 2022 Media
drwxrwxr-x 6 domain users 4096 Nov 9 2022 Messenger
drwxrwxr-x 6 domain users 4096 Nov 9 2022 Notification
drwxrwxr-x 3 domain users 4096 Jun 7 2022 Overview
drwxrwxr-x 5 domain users 4096 Nov 9 2022 Radio
drwxrwxr-x 6 domain users 4096 Nov 9 2022 Settings
drwxrwxr-x 3 domain users 4096 Jun 7 2022 Stream
drwxrwxr-x 5 domain users 4096 May 11 2023 SystemUpdater
drwxrwxr-x 5 domain users 4096 Nov 9 2022 tests
原生提供了许多应用,介绍如下:
应用 | 描述 |
---|---|
Cluster | 仪表板 |
Dialer | 电话 |
Hvac | 空调 |
LatinIME | 输入法 |
Launcher | 桌面 |
LensPicker | 活动窗口选择应用(Launcher) |
LinkViewer | 二维码 |
LocalMediaPlayer | 本地播放器 |
Media | 媒体应用 |
Messenger | 消息管理应用(消息及TTS) |
Notification | 通知 |
Overview | 音源声卡选择应用 |
Radio | 收音机 |
Settings | 设置 |
Stream | 音源管理应用 |
SystemUpdater | 系统升级 |
test | 测试 |
libs | 支持库 |
2、CarServiceApi
2.1 代码路径
CarServiceApi 代码路径为 /android/packages/services/Car/car-lib。
ubuntu16-017:/android/packages/services/Car/car-lib$ ls -l
total 32
-rw-r--r-- 1 domain users 7494 Nov 9 2022 Android.bp
-rw-r--r-- 1 domain users 932 Nov 9 2022 AndroidManifest.xml
-rw-r--r-- 1 domain users 1535 Nov 9 2022 Android.mk
drwxrwxr-x 4 domain users 4096 Nov 9 2022 api
drwxrwxr-x 4 domain users 4096 Jun 7 2022 native
drwxrwxr-x 4 domain users 4096 Jun 7 2022 src
drwxrwxr-x 3 domain users 4096 Jun 7 2022 src_feature_future
如上所示,Android.bp 是 car-lib 的编译文件;AndroidManifest.xml 是编译配置文件;Android.mk 描述了添加 car-lib 到系统编译中;api 文件夹中描述了 car-lib 所支持的接口描述 txt 文件;native 文件夹中保存了 car-lib 所需的 libcarpowermanager 库,关于 CarPower 模块的回调信息;src 中描述了 car-lib 提供的接口代码;src_feature_future 文件夹下主要保存 FeatureConfiguration.java 文件,以静态变量的方式保存有关 feature 的默认状态。
2.2 Api 介绍
2.2.1 编译
2.2.1.1 编译命令
编译 Carlib 有三种不同的指令
2.2.1.1.1 make android.car
android.car 编译出的 Carlib 库包含 Car API 中定义的所有方法和实现细节。
编译结果保存到:/out/target/common/obj/JAVA_LIBRARIES/android.car_intermediates 路径下。
2.2.1.1.2 make android.car-system-stubs
android.car-system-stubs 包含了 Car 服务端的 API 接口定义,但不包含实际的实现代码。主要用于在开发 Car 服务时进行编译和链接,以便在编译时检查代码的正确性。确保 Car 服务与 Car 应用之间的通信接口正确对接。
2.2.1.1.3 make android.car-stubs
make android.car-stubs 包含了 Car 应用的 API 接口定义,但并不包含实际的实现代码。主要用于在开发 Car 应用时进行编译和链接,以便在编译时检查代码的正确性。确保 Car 应用与 Car 服务之间的通信接口正确对接。
2.2.1.2 编译文件
下面通过 Android.bp 文件分析下 car-lib 编译逻辑:
// 编译 libcarpowermanager 静态库,主要编译文件是 native 目录下的 CarPowerManager 相关文件
cc_library {name: "libcarpowermanager",aidl: {export_aidl_headers: true,local_include_dirs: ["src",],},cflags: ["-Wall","-Werror","-Wextra","-Wno-unused-parameter",],include_dirs: ["packages/services/Car/car-lib/native/include",],shared_libs: ["libbinder","liblog","libutils",],srcs: ["src/android/car/ICar.aidl","src/android/car/hardware/power/ICarPower.aidl","src/android/car/hardware/power/ICarPowerStateListener.aidl","native/CarPowerManager/CarPowerManager.cpp",],
}java_library {name: "android.car.cluster.navigation",proto: {type: "lite",},static_libs: ["libprotobuf-java-lite"],srcs: ["src/android/car/navigation/navigation_state.proto"]
}// android.car 库,是 Carlib API 库
java_library {name: "android.car",srcs: ["src/**/*.java","src_feature_future/**/*.java","src/**/I*.aidl",],static_libs: ["android.hardware.automotive.YFvehicle-V2.0-java"],aidl: {include_dirs: ["system/bt/binder",],},exclude_srcs: ["src/android/car/storagemonitoring/IoStats.aidl","src/android/car/storagemonitoring/IoStatsEntry.aidl",],product_variables: {pdk: {enabled: false,},},installable: true,
}// android.car-stubs 库,Car 应用端的 API 接口定义,不包含实现代码逻辑。
java_library_static {name: "android.car-stubs",srcs: [":android.car-stub-docs",],libs: ["android.car",],product_variables: {pdk: {enabled: false,},},compile_dex: true,dist: {targets: ["dist_files"],}
}// android.car-system-stubs 库。Car 服务端 API 接口定义,不包含实现代码逻辑。
java_library_static {name: "android.car-system-stubs",srcs: [":android.car-system-stubs-docs",],libs: ["android.car",],product_variables: {pdk: {enabled: false,},},compile_dex: true,dist: {targets: ["dist_files"],}
}
在源码中添加 AndroidAutomotive 相关编译,包括 APP、Jar 包、CarService 服务等,编译文件在 /android/packages/services/Car/car_product/build/car.mk 文件中。
# Automotive specific packages 添加 Automotive 应用编译
PRODUCT_PACKAGES += \CarFrameworkPackageStubs \CarService \CarDialerApp \OverviewApp \CarLauncher \CarSystemUI \LocalMediaPlayer \CarMediaApp \CarMapsPlaceholder \CarSettings \android.car \car-frameworks-service \com.android.car.procfsinspector \libcar-framework-service-jni \CarHvacApp \# should add to BOOT_JARS only once 添加 Carlib Jar 包编译
ifeq (,$(INCLUDED_ANDROID_CAR_TO_PRODUCT_BOOT_JARS))
PRODUCT_BOOT_JARS += \android.carPRODUCT_HIDDENAPI_STUBS := \android.car-stubsPRODUCT_HIDDENAPI_STUBS_SYSTEM := \android.car-system-stubsPRODUCT_HIDDENAPI_STUBS_TEST := \android.car-test-stubs
2.2.2 Api 分类
Android Automotive 的 carlib 是一个用于 Car 应用开发的库,提供了一系列 API 接口供开发者使用。
CarApi 类图如下所示:
下表是所有 Car-lib API 接口描述:
接口类 | 说明 |
---|---|
CarAudioManager | 用于管理车辆音频统的接口,可以控制音量、音频输入输出等功能 |
CarSensorManager | 用于访问车辆传感器数据的接口,可以获取车辆的加速度、转向角度、车辆倾斜角度等传感器数据 |
CarInfoManager | 用于获取车辆信息的接口,可以获取车辆的VIN码、车辆速度、车辆位置等信息 |
CarDiagManager | 用于控制汽车EOL/Diag系统的接口 |
CarAppFocusManager | 设置和监听当前应用焦点的接口 |
CarPackageManager | 提供专用的和包管理相关的接口 |
CarNavigationStatusManager | 为仪表盘提供导航状态的接口 |
CarCabinManager | 控制汽车座舱系统的接口(CarPropertyManager 代替) |
CarDiagnosticManager | 监控诊断数据的接口 |
CarHvacManager | 控制空调系统的接口(CarPropertyManager 代替) |
CarPowerManager | 接收电源状态变化的接口 |
CarProjectionManager | 投屏管理接口 |
CarPropertyManager | 与车辆属性交互的接口 |
CarVendorExtensionManager | 访问厂商扩展车辆属性的接口(CarPropertyManager 代替) |
CarInstrumentClusterManager | 配合仪表控制的接口(CarPropertyManager 代替) |
CarTestManagerBinderWrapper | 仅用于系统测试 |
VmsSubscriberManager | 供地图服务订阅者使用的接口 |
CarBluetoothManager | 提供和特定的汽车蓝牙管理器交互的接口 |
CarStorageMonitoringManager | 检索闪存信息的接口 |
CarDrivingStateManager | 获取与驾驶状态相关信息的接口 |
CarUxRestrictionsManager | 获取驾驶过程中用户体验限制的接口 |
CarConfigurationManager | 显示存储在系统中的车辆配置值的接口 |
CarTrustAgentEnrollmentManager | 授权可信任设备的接口 |
CarMediaManager | 接收媒体音源变化的接口 |
CarBugreportManager | 报告错误的接口 |
Car-lib 常用的接口类有:
- CarApiManager:
用于管理Car API的接口,可以通过该接口获取Car的各种功能模块,如 CarInfoManager、 CarSensorManager 等。
- CarInfoManager:
用于获取车辆信息的接口,可以获取车辆的VIN码、车辆速度、车辆位置等信息。
- CarSensorManager:
用于访问车辆传感器数据的接口,可以获取车辆的加速度、转向角度、车辆倾斜角度等传感器数据。
- CarAudioManager:
用于管理车辆音频系统的接口,可以控制音量、音频输入输出等功能。
- CarNavigationManager:
用于车辆导航功能的接口,可以获取车辆当前位置、目的地信息等。
- CarPackageManager:
用于管理Car应用包的接口,可以安装、卸载、启动 Car 应用。
- CarProjectionManager:
用于管理车辆投影功能的接口,可以控制车辆的投影屏幕显示。
- CarInputManager:
用于处理车辆输入事件的接口,可以处理车辆上的按键事件、触摸事件等。
2.3 Api 使用方法
CarService 中各个服务本质上是 AIDL 接口的实现类,属于 Server 端,而对应的 Client 端就需要一个 IBinder 对象来访问 Server 端的方法,这些 IBinder 对象在 Car API 中被封装在一个个 XXXManager 类中。
Car API 与 CarService 中的服务,名称上存在对应关系,所以很好理解。例如:CarWatchdogManager 对应 CarWatchdogService,CarMediaManager 对应 CarMediaService。
不过也有例外:CarInfoManager、CarSensorManager、CarHvacManager、CarCabinManager、CarVendorExtensionManager 都对应 CarPropertyService。但是在 Android 11 中这些 Manager 都已经过时,Google 建议统一使用 CarPropertyManager。
对于 Car SDK 的调用,在 Android 9 与 Android 10 版本略有不同。
2.3.1 Car 类
Car 类是 Android Automotive 的 API 接口,Car 是 Car-lib 总功能的入口,Car 类中包含许多的 Manager,通过 getCarManager(String) 获取 Car-lib 提供的接口对象。
常用方法如下:
方法 | 描述 |
---|---|
public static Car createCar(Context context) | 获取Car实例 |
public static Car createCar(Context context, @Nullable Handler handler) | 获取Car实例 |
public static Car createCar(@NonNull Context context, @Nullable Handler handler, long waitTimeoutMs, @NonNull CarServiceLifecycleListener statusChangeListener) | 获取Car实例 |
public Object getCarManager(String serviceName) | 获取子Manager |
public boolean isConnected() | 判断是否和CarService 保持连接 |
public void disconnect() | 断开和CarService 的连接 |
2.3.1.1 createCar()
createCar() 函数是应用创建 Car 对象的调用方法,这里来分析下 createCar() 函数的代码流程,下面是 /android/packages/services/Car/car-lib/src/android/car/Car.java 文件内容
public static Car createCar(Context context, @Nullable Handler handler) {Log.i(TAG_CAR, "createCar");Car car = null;IBinder service = null;boolean started = false;int retryCount = 0;while (true) {service = ServiceManager.getService(CAR_SERVICE_BINDER_SERVICE_NAME);if (car == null) {// service can be still null. The constructor is safe for null service.car = new Car(context, ICar.Stub.asInterface(service),null /*serviceConnectionListener*/, null /*statusChangeListener*/, handler);}if (service != null) {if (!started) { // specialization for most common case.return car;}break;}if (!started) {car.startCarService();started = true;}retryCount++;if (retryCount > CAR_SERVICE_BINDER_POLLING_MAX_RETRY) {Log.e(TAG_CAR, "cannot get car_service, waited for car service (ms):"+ CAR_SERVICE_BINDER_POLLING_INTERVAL_MS* CAR_SERVICE_BINDER_POLLING_MAX_RETRY,new RuntimeException());return null;}try {Thread.sleep(CAR_SERVICE_BINDER_POLLING_INTERVAL_MS);} catch (InterruptedException e) {Log.e(CarLibLog.TAG_CAR, "interrupted while waiting for car_service",new RuntimeException());return null;}}// Can be accessed from mServiceConnectionListener in main thread.synchronized (car) {if (car.mService == null) {car.mService = ICar.Stub.asInterface(service);Log.w(TAG_CAR,"waited for car_service (ms):"+ CAR_SERVICE_BINDER_POLLING_INTERVAL_MS * retryCount,new RuntimeException());}car.mConnectionState = STATE_CONNECTED;}return car;}
1、通过一个 while 循环一直去创建 Car 实例,并且建立与 CarService 服务的连接,直到实例化成功或者创建次数超过最大限制 CAR_SERVICE_BINDER_POLLING_MAX_RETRY(100)。
2、service = ServiceManager.getService(CAR_SERVICE_BINDER_SERVICE_NAME);首先获取CarService的服务实例
3、car = new Car(context, ICar.Stub.asInterface(service), null , null, handler); 这里前面方法中声明,所以判断一定是null,会new一个Car实例。
4、car.startCarService(); 这里实际是去用Bind的方式启动 CarService。
5、if (retryCount > CAR_SERVICE_BINDER_POLLING_MAX_RETRY) { 这里会有个判断重试的次数,如果超过次数还没取到 CarService 服务,则会返回 null。sleep 的时间是 50ms,重试 100 次,也就是获取 Car 实例最长时间长达 5 s。
2.3.1.2 startCarService()
startCarService() 函数实际是调用 Context.bindServiceAsUser() 函数去启动 CarService 服务。
下面是 startCarService() 函数逻辑:
private void startCarService() {Intent intent = new Intent();intent.setPackage(CAR_SERVICE_PACKAGE);intent.setAction(Car.CAR_SERVICE_INTERFACE_NAME);boolean bound = mContext.bindServiceAsUser(intent, mServiceConnectionListener,Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF);Log.i(TAG_CAR, "bindServiceAsUser bound" +bound);synchronized (mLock) {if (!bound) {Log.i(TAG_CAR, "bindServiceAsUser 1");mConnectionRetryCount++;if (mConnectionRetryCount > CAR_SERVICE_BIND_MAX_RETRY) {Log.w(TAG_CAR, "cannot bind to car service after max retry");mMainThreadEventHandler.post(mConnectionRetryFailedRunnable);} else {Log.i(TAG_CAR, "bindServiceAsUser 2");mEventHandler.postDelayed(mConnectionRetryRunnable,CAR_SERVICE_BIND_RETRY_INTERVAL_MS);}} else {Log.i(TAG_CAR, "bindServiceAsUser 3");mEventHandler.removeCallbacks(mConnectionRetryRunnable);mMainThreadEventHandler.removeCallbacks(mConnectionRetryFailedRunnable);mConnectionRetryCount = 0;mServiceBound = true;}}}
如果调用 Context.bindServiceAsUser() 启动 CarService 服务失败,会重试连接 CAR_SERVICE_BIND_MAX_RETRY (20) 次。超过尝试次数后会调用 mConnectionRetryFailedRunnable。
2.3.1.3 connect()
connect() 方法实际就是调用 startCarService() 方法,因为 connect() 是公开的,startCarService() 是私有的。connect() 函数只能调用一次,如果当前已经处于连接状态,再次调用 connect() 函数会抛 IllegalStateException 异常,client 没有捕获该异常,则会引起 client 程序的崩溃。
public void connect() throws IllegalStateException {synchronized (mLock) {if (mConnectionState != STATE_DISCONNECTED) {throw new IllegalStateException("already connected or connecting");}mConnectionState = STATE_CONNECTING;startCarService();}}
connect() 已经弃用了,我们知道 startCarService() 就是和 CarService 建立连接,因为在 CreatCar() 里已经保证获取到 CarService,所以 connect() 不在必要。
2.3.2 示例
通过 Car.createCar() 函数可以创建 Car 对象,参数需要传入 ServiceConnection,并在 service 连接成功后,获取想要的 Manager 实例,调用 connect() 函数,当与 CarService 连接成功后,会回调 ServiceConnection 对象的 onServiceConnected() 函数完成连接。如下所示:
private final ServiceConnection mCarServiceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {try {CarHvacManager manager = (CarHvacManager) mCarApiClient.getCarManager(Car.HVAC_SERVICE);} catch (CarNotConnectedException e) {Log.e(TAG, "Car not connected in onServiceConnected");}}@Overridepublic void onServiceDisconnected(ComponentName name) {}
};...
Car carApiClient = Car.createCar(context, mCarServiceConnection);
carApiClient.connect();
...
但是在 Android 10 以后,Google 改写了 Car API 的使用方式,Android 10 以后构建 Car 对象不再建议传入 ServiceConnection 对象,而是使用了下面的方法。
Car carApiClient = Car.createCar(context);
CarHvacManager manager = (CarHvacManager) mCarApiClient.getCarManager(Car.HVAC_SERVICE);
Android 10 以后 Car API 的调用方式由异步方法改为了同步方法,保留了 disconnect(),但是不需要调用 connect(),这样使用起来更加简单。
这种调用方式是 CarService 不发生异常的情况下,Client 端与 CarService 的连接也不会断开,但是如果 CarService 发生异常,Client 端会被直接杀死,使用如下方式可以避免:
Car car = Car.createCar(this, workThreadHandler, 2000, new Car.CarServiceLifecycleListener() {@Overridepublic void onLifecycleChanged(@NonNull Car car, boolean ready) {// ready 在Service断开连接时会变为false
if (ready) {} else {// CarService 发生异常或连接被断开了,需要client端处理。
}}
});
通过创建 Car 对象时传入 CarServiceLifecycleListener 对象,注册回调的方式,通过回调以知道 CarService 的生命周期。
3、总结
本篇文档介绍了 AndroidAutomotive 架构中 APP 应用与 API 接口等内容,对于应用分类、功能、代码;API 接口介绍、初始化、使用方法等。
上一篇:AndroidAutomotive模块介绍(一)整体介绍
下一篇:AndroidAutomotive模块介绍(三)CarService服务