作者:TechMerger
本文将结合 Android 系统整体,对 CarService 的构成和链路对其做一个全面的分析和理解。
SystemServer
中专门为了 Automotive OS 设立的系统服务,用来管理车机的核心服务 CarService
。该系统服务的具体实现实际上由 CarServiceHelperServiceUpdatableImpl
类完成,后面会提到。
System service side companion service for CarService. Starts car service and provide necessary API for CarService. Only for car product.
Car Service App 实际上分为两个,一个是和系统服务直接交互的 builtin app,另一个是给该 built app 提供实际实现的 updatable app。
系统中与车相关的核心 App,掌管最重要的 CarService
服务类。目录位于:
其 AndroidManifest.xml 文件如下,可以看到它具有系统权限、与 system uid 共享数据。
...
builtin app 的所有具体实现以及后续支持的一堆服务都在 updatable app 中实现,目录见:
其 AndroidManifest.xml 文件如下,可以看到它也具有系统权限。
...
Android 车机里提供给系统使用汽车相关能力的专用接口,源码实现在:
看下它的 manifest 文件:
再看下它的 bp 文件:
...
filegroup {
name: "android.car-full-src",
srcs: [
"src/**/*.java",
"src/**/*.aidl",
],
visibility: [
"//packages/services/Car/car-lib",
"//packages/services/Car/car-lib-module",
],
}
java_library {
name: "android.car",
srcs: [
":android.car-full-src",
],
aidl: {
include_dirs: [
"packages/modules/Bluetooth/framework/aidl-export",
],
},
libs: [
"android.car.builtin",
"framework-annotations-lib",
"modules-utils-preconditions",
"framework-wifi",
"framework-bluetooth",
],
installable: true,
sdk_version: "module_current",
}
可以看到它会编译到 android.car.jar 中,而非面向 AOSP 手机/平板的 android.jar 中。这也意味着如果要基于 Car 相关 API 开发,需要通过添加 Automotive os addon 的方式才能导入该 SDK。
这个 jar 囊括了我们常用的 Car、CarPowerManager、CarSettings 等,下面罗列了部分常用的 Car API:
最后的实现会经由 AIDL 抵达上个章节的 CarService App。
提供 Automotive OS 内置的、专门为 Car 场景设计的 App,目录位于:
比如面向 Car 的 SystemUI、Launcher、Settings、IME 等。
SystemServer
会在启动系统服务的过程中依据 FEATURE_AUTOMOTIVE 的特性决定是否启动 AAOS 的系统服务 CarServiceHelperService
。
public final class SystemServer implements Dumpable {
...
private static final String CAR_SERVICE_HELPER_SERVICE_CLASS =
"com.android.internal.car.CarServiceHelperService";
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
t.traceBegin("startOtherServices");
...
mActivityManagerService.systemReady(() -> {
Slog.i(TAG, "Making services ready");
...
boolean isAutomotive = mPackageManager
.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
if (isAutomotive) {
t.traceBegin("StartCarServiceHelperService");
final SystemService cshs = mSystemServiceManager
.startService(CAR_SERVICE_HELPER_SERVICE_CLASS);
t.traceEnd();
}
...
}, t);
...
}
...
}
CarServiceHelperService 执行 onStart() 的时候,会将后续的工作交给 CarServiceHelperServiceUpdatableImpl
来处理。
其 onStart() 里调用 bindService()
绑定 action 为 “android.car.ICar”、package 为 “com.android.car” 的 Service,即构成章节里提到的 CarService
组件。
public class CarServiceHelperService extends SystemService
implements Dumpable, DevicePolicySafetyChecker, CarServiceHelperInterface {
...
@Override
public void onStart() {
...
mCarServiceHelperServiceUpdatable.onStart();
}
...
}
public final class CommonConstants {
...
// CarService Constants
public static final String CAR_SERVICE_INTERFACE = "android.car.ICar";
...
}
public final class CarServiceHelperServiceUpdatableImpl
implements CarServiceHelperServiceUpdatable, Executor {
...
private static final String CAR_SERVICE_PACKAGE = "com.android.car";
@Override
public void onStart() {
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");
}
}
...
}
CarService
的实现都在父类 ServiceProxy
中,比如首先被调用的 onCreate(),内部将先调用 init()。
init() 将构建 mRealServiceClassName 的实例,而 mRealServiceClassName 变量来自于 UpdatablePackageDependency.java
中定义的 CAR_SERVICE_IMPL_CLASS 常量即 “com.android.car.CarServiceImpl”。
通过如下代码看到,这意味着将初始化 CarServiceImpl
实例。
public class CarService extends ServiceProxy {
...
public CarService() {
super(UpdatablePackageDependency.CAR_SERVICE_IMPL_CLASS);
// Increase the number of binder threads in car service
BinderInternal.setMaxThreads(MAX_BINDER_THREADS);
}
...
}
public class ServiceProxy extends Service {
...
@Override
public void onCreate() {
init();
mRealService.onCreate();
}
private void init() {
mUpdatablePackageContext = UpdatablePackageContext.create(this);
try {
mRealServiceClass = mUpdatablePackageContext.getClassLoader().loadClass(
mRealServiceClassName);
// Use default constructor always
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 UpdatablePackageDependency {
/** {@code com.android.car.CarServiceImpl} class */
public static final String CAR_SERVICE_IMPL_CLASS = "com.android.car.CarServiceImpl";
...
}
init() 之后是执行创建出来的 CarServiceImpl 实例的 onCreate(),可以看到是继续创建关键类 ICarImpl
的实例并再次执行 init()。
public class CarServiceImpl extends ProxiedService {
...
@Override
public void onCreate() {
...
mICarImpl = new ICarImpl(this,
getBuiltinPackageContext(),
mVehicle,
SystemInterface.Builder.defaultSystemInterface(this).build(),
mVehicleInterfaceName);
mICarImpl.init();
...
}
...
}
public class ICarImpl extends ICar.Stub {
...
ICarImpl( ... ) {
...
mHal = constructWithTrace(t, VehicleHal.class,
() -> new VehicleHal(serviceContext, vehicle));
...
mCarPropertyService = constructWithTrace(
t, CarPropertyService.class,
() -> new CarPropertyService(serviceContext, mHal.getPropertyHal()));
mCarDrivingStateService = constructWithTrace(
t, CarDrivingStateService.class,
() -> new CarDrivingStateService(serviceContext, mCarPropertyService));
...
mCarPowerManagementService = constructWithTrace(
t, CarPowerManagementService.class,
() -> new CarPowerManagementService(mContext, mHal.getPowerHal(),
systemInterface, mCarUserService, powerPolicyDaemon));
...
mCarInputService = constructWithTrace(t, CarInputService.class,
() -> new CarInputService(serviceContext, mHal.getInputHal(), mCarUserService,
mCarOccupantZoneService, mCarBluetoothService));
...
List allServices = new ArrayList<>();
allServices.add(mCarPropertyService); // mCarUXRestrictionsService depends on it
allServices.add(mCarPowerManagementService);
allServices.add(mCarDrivingStateService);
allServices.add(mCarInputService);
...
...
mAllServices = allServices.toArray(new CarServiceBase[allServices.size()]);
mICarSystemServerClientImpl = new ICarSystemServerClientImpl();
...
}
...
}
ICarImpl 的初始化将完成很多 vehicle 相关的重要工作:
VehicleHal
实例CarInputService
CarPropertyService
CarPowerManagementService
CarDrivingStateService
public class ICarImpl extends ICar.Stub {
...
void init() {
...
mHal.init();
for (CarServiceBase service : mAllServices) {
service.init();
}
...
}
...
}
下面以 CarInputService 和 CarPropertyService 为例,继续看某个具体的 CarServiceBase 初始化了什么。
CarInputService
初始化是向和 VehicleHal 交互的 InputHalService
传递输入相关的监听器 Listener。
public class CarInputService ... {
...
@Override
public void init() {
if (!mInputHalService.isKeyInputSupported()) {
return;
}
mInputHalService.setInputListener(this);
...
}
...
}
InputHalService 将依据是否支持按键输入、旋钮输入、自定义输入的配置决定是否向 VehicleHal 订阅 Input Property 变化。
public class InputHalService extends HalServiceBase {
...
private final VehicleHal mHal;
public void setInputListener(InputListener listener) {
boolean keyInputSupported;
boolean rotaryInputSupported;
boolean customInputSupported;
synchronized (mLock) {
if (!mKeyInputSupported && !mRotaryInputSupported && !mCustomInputSupported) {
Slogf.w(TAG, "input listener set while rotary and key input not supported");
return;
}
mListener = listener;
keyInputSupported = mKeyInputSupported;
rotaryInputSupported = mRotaryInputSupported;
customInputSupported = mCustomInputSupported;
}
if (keyInputSupported) {
mHal.subscribeProperty(this, HW_KEY_INPUT);
}
if (rotaryInputSupported) {
mHal.subscribeProperty(this, HW_ROTARY_INPUT);
}
if (customInputSupported) {
mHal.subscribeProperty(this, HW_CUSTOM_INPUT);
}
}
...
}
CarPropertyService 也是一样,向和 VehicleHal 打交道的 PropertyHalService
传递输入相关的监听器 Listener。
public class CarPropertyService extends ICarProperty.Stub
implements CarServiceBase, PropertyHalService.PropertyHalListener {
@Override
public void init() {
synchronized (mLock) {
// Cache the configs list and permissions to avoid subsequent binder calls
mConfigs = mHal.getPropertyList();
mPropToPermission = mHal.getPermissionsForAllProperties();
if (DBG) {
Slogf.d(TAG, "cache CarPropertyConfigs " + mConfigs.size());
}
}
mHal.setListener(this);
}
...
}
PropertyHalService 将 CarPropertyService 作为 Callback 暂存,等待来自 HAL 的 Vehicle Property 变化回调。
public class PropertyHalService extends HalServiceBase {
/**
* Set the listener for the HAL service
* @param listener
*/
public void setListener(PropertyHalListener listener) {
synchronized (mLock) {
mListener = listener;
}
}
...
}
对于其他 App 来说,想要使用 Car API,得做些准备工作:
比如这段用 CarPropertyManager 的示例:
private Car mCar;
private CarPropertyManager mCarPropertyManager;
mCar = Car.createCar(this, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
(car, ready) -> {
mCar = car;
if (ready) {
mCarPropertyManager =
(CarPropertyManager) mCar.getCarManager(Car.PROPERTY_SERVICE);
...
}
});
我们结合源码看下上面这个步骤的链路:
createCar()
首先检查当前 OS 是否属于 Automotive 版本,之后调用 Car 构造函数进行一些全局变量的准备。
public final class Car {
...
public static Car createCar(Context context, ServiceConnection serviceConnectionListener,
@Nullable Handler handler) {
if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
Log.e(TAG_CAR, "FEATURE_AUTOMOTIVE not declared while android.car is used");
return null;
}
try {
return new Car(context, /* service= */ null , serviceConnectionListener,
/* statusChangeListener= */ null, handler);
} catch (IllegalArgumentException e) {
// Expected when car service loader is not available.
}
return null;
}
private Car( ... ) {
mContext = context;
mEventHandler = determineEventHandler(handler);
mMainThreadEventHandler = determineMainThreadEventHandler(mEventHandler);
mService = service;
if (service != null) {
mConnectionState = STATE_CONNECTED;
} else {
mConnectionState = STATE_DISCONNECTED;
}
mServiceConnectionListenerClient = serviceConnectionListener;
mStatusChangeCallback = statusChangeListener;
// Store construction stack so that client can get help when it crashes when car service
// crashes.
if (serviceConnectionListener == null && statusChangeListener == null) {
mConstructionStack = new RuntimeException();
} else {
mConstructionStack = null;
}
}
...
}
connect()
首先检查是否重复请求连接了,确实需要连接的话调用 startCarService()
核心处理。
startCarService() 将绑定 CarService 服务。
public final class Car {
...
public void connect() throws IllegalStateException {
synchronized (mLock) {
if (mConnectionState != STATE_DISCONNECTED) {
throw new IllegalStateException("already connected or connecting");
}
mConnectionState = STATE_CONNECTING;
startCarService();
}
}
private void startCarService() {
Intent intent = new Intent();
intent.setPackage(CAR_SERVICE_PACKAGE);
intent.setAction(Car.CAR_SERVICE_INTERFACE_NAME);
boolean bound = mContext.bindService(intent, mServiceConnectionListener,
Context.BIND_AUTO_CREATE);
synchronized (mLock) {
if (!bound) {
mConnectionRetryCount++;
if (mConnectionRetryCount > CAR_SERVICE_BIND_MAX_RETRY) {
Log.w(TAG_CAR, "cannot bind to car service after max retry");
mMainThreadEventHandler.post(mConnectionRetryFailedRunnable);
} else {
mEventHandler.postDelayed(mConnectionRetryRunnable,
CAR_SERVICE_BIND_RETRY_INTERVAL_MS);
}
} else {
mEventHandler.removeCallbacks(mConnectionRetryRunnable);
mMainThreadEventHandler.removeCallbacks(mConnectionRetryFailedRunnable);
mConnectionRetryCount = 0;
mServiceBound = true;
}
}
}
...
}
绑定成功之后,将 ICar AIDL 的本地接口代理赋值到 mService 变量(server 端在 CarService 的 ICarImpl
中),待使用。
public final class Car {
...
private final ServiceConnection mServiceConnectionListener =
new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mLock) {
ICar newService = ICar.Stub.asInterface(service);
if (mService != null && mService.asBinder().equals(newService.asBinder())) {
// already connected.
return;
}
mConnectionState = STATE_CONNECTED;
mService = newService;
}
if (mStatusChangeCallback != null) {
mStatusChangeCallback.onLifecycleChanged(Car.this, true);
} else if (mServiceConnectionListenerClient != null) {
mServiceConnectionListenerClient.onServiceConnected(name, service);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
...
};
...
}
getCarManager() 首先得确保 CarService 准备就绪了,然后再去缓存 Manager 实例的 HashMap mServiceMap 中查找是否已有现成的,最后才发起 AIDL 获取该功能的接口。
public final class Car {
...
private final HashMap mServiceMap = new HashMap<>();
public Object getCarManager(String serviceName) {
CarManagerBase manager;
synchronized (mLock) {
if (mService == null) {
Log.w(TAG_CAR, "getCarManager not working while car service not ready");
return null;
}
manager = mServiceMap.get(serviceName);
if (manager == null) {
try {
IBinder binder = mService.getCarService(serviceName);
if (binder == null) {
Log.w(TAG_CAR, "getCarManager could not get binder for service:"
+ serviceName);
return null;
}
manager = createCarManagerLocked(serviceName, binder);
if (manager == null) {
Log.w(TAG_CAR, "getCarManager could not create manager for service:"
+ serviceName);
return null;
}
mServiceMap.put(serviceName, manager);
} catch (RemoteException e) {
handleRemoteExceptionFromCarService(e);
}
}
}
return manager;
}
...
}
ICarImpl 则是依据约定好的 Manager 的 Service 端名称去 CarService 中返回启动时候创建的一堆具体服务。
比如:PROPERTY_SERVICE 的话返回 CarPropertyService
,CAR_INPUT_SERVICE 返回 CarInputService
。
public class ICarImpl extends ICar.Stub {
...
public IBinder getCarService(String serviceName) {
if (!mFeatureController.isFeatureEnabled(serviceName)) {
Slogf.w(CarLog.TAG_SERVICE, "getCarService for disabled service:" + serviceName);
return null;
}
switch (serviceName) {
...
case Car.PROPERTY_SERVICE:
case Car.SENSOR_SERVICE:
case Car.VENDOR_EXTENSION_SERVICE:
return mCarPropertyService;
...
case Car.CAR_INPUT_SERVICE:
return mCarInputService;
...
default:
IBinder service = null;
if (mCarExperimentalFeatureServiceController != null) {
service = mCarExperimentalFeatureServiceController.getCarService(serviceName);
}
if (service == null) {
Slogf.w(CarLog.TAG_SERVICE, "getCarService for unknown service:"
+ serviceName);
}
return service;
}
}
...
}
Automotive OS 提供了 CarPropertyManager
API 给第三方 App 读写车辆属性。参数 propId 来自于 VehiclePropertyIds 类中定义的属性 ID,当然也需要获得相应的 permission。
比如读写车窗属性的 ID 为 WINDOW_POS,需要 android.car.permission.CONTROL_CAR_WINDOWS 的权限。
读取 getProperty()
首先调用 checkSupportedProperty() 检查是否支持该属性,如果是 USER 相关的属性会抛出如下的异常:
Unsupported property:xxx
通过检查的向实现的 Service 发出读取调用。
public class CarPropertyManager extends CarManagerBase {
public CarPropertyValue getProperty(@NonNull Class clazz, int propId, int areaId) {
checkSupportedProperty(propId);
try {
CarPropertyValue propVal = mService.getProperty(propId, areaId);
if (propVal != null && propVal.getValue() != null) {
Class> actualClass = propVal.getValue().getClass();
}
return propVal;
}
...
}
private void checkSupportedProperty(int propId) {
switch (propId) {
case VehiclePropertyIds.INITIAL_USER_INFO:
case VehiclePropertyIds.SWITCH_USER:
case VehiclePropertyIds.CREATE_USER:
case VehiclePropertyIds.REMOVE_USER:
case VehiclePropertyIds.USER_IDENTIFICATION_ASSOCIATION:
throw new IllegalArgumentException("Unsupported property: "
+ VehiclePropertyIds.toString(propId) + " (" + propId + ")");
}
}
...
}
ICarProperty aidl 的实现即位于上个章节分析到的 CarPropertyService
中。
1.先到存放所有 Property ID 的 SparseArray
中检查是否确实存在该 Property,如果不存在的话打印 error 提醒并结束
2.获取该 Property 的 Permission 配置,如果不存在的话,抛出如下异常:
> SecurityException: Platform does not have permission to read value for property Id: 0x...
3.assertPermission() 检查当前 Car Service 是否确实被授予了如上 Permission
4.最后调用持有的 PropertyHalService 继续发出读取的调用
public class CarPropertyService extends ICarProperty.Stub
implements CarServiceBase, PropertyHalService.PropertyHalListener {
@Override
public CarPropertyValue getProperty(int prop, int zone) ... {
synchronized (mLock) {
if (mConfigs.get(prop) == null) {
// Do not attempt to register an invalid propId
Slogf.e(TAG, "getProperty: propId is not in config list:0x" + toHexString(prop));
return null;
}
}
// Checks if android has permission to read property.
String permission = mHal.getReadPermission(prop);
if (permission == null) {
throw new SecurityException("Platform does not have permission to read value for "
+ "property Id: 0x" + Integer.toHexString(prop));
}
CarServiceUtils.assertPermission(mContext, permission);
return runSyncOperationCheckLimit(() -> {
return mHal.getProperty(prop, zone);
});
}
...
}
PropertyHalService 首先调用 managerToHalPropId() 将 Property ID 转为 HAL 中该 ID 的定义,并再度检查该 HAL ID 是否确实存在。如果不存在的话亦抛出异常:
IllegalArgumentException:Invalid property Id : 0x…
接着,通过 VehicleHal
传递 HAL 中 ID 继续读取得到 HalPropValue
,当读取的 value 存在的话,首先得获取该 Property 在 HAL 层和上层定义的 HalPropConfig
规则。
最后依据 config 将 value 解析成 CarPropertyValue
类型返回。
public class PropertyHalService extends HalServiceBase {
'/ ' ...
public CarPropertyValue getProperty(int mgrPropId, int areaId)
throws IllegalArgumentException, ServiceSpecificException {
int halPropId = managerToHalPropId(mgrPropId);
if (!isPropertySupportedInVehicle(halPropId)) {
throw new IllegalArgumentException("Invalid property Id : 0x" + toHexString(mgrPropId));
}
// CarPropertyManager catches and rethrows exception, no need to handle here.
HalPropValue value = mVehicleHal.get(halPropId, areaId);
if (value == null) {
return null;
}
HalPropConfig propConfig;
synchronized (mLock) {
propConfig = mHalPropIdToPropConfig.get(halPropId);
}
return value.toCarPropertyValue(mgrPropId, propConfig);
}
...
}
其实 VehicleHal 并未做太多处理就直接交给了 HalClient 来处理。
public class VehicleHal implements HalClientCallback {
...
public HalPropValue get(int propertyId)
throws IllegalArgumentException, ServiceSpecificException {
return get(propertyId, NO_AREA);
}
...
public HalPropValue get(int propertyId, int areaId)
throws IllegalArgumentException, ServiceSpecificException {
return mHalClient.getValue(mPropValueBuilder.build(propertyId, areaId));
}
...
}
HalClient
通过 invokeRetriable()
进行超时为 50ms 的 internalGet()
调用:如果结果是 TRY_AGAIN 并且尚未超时的话,再次调用;反之已经超时或者结果成功获取到的话,即结束。
后续会再次检查该 Result 中的 status,是否是不合法的、空的值等等,通过检查的话则返回 HalPropValue 出去。
final class HalClient {
...
private static final int SLEEP_BETWEEN_RETRIABLE_INVOKES_MS = 50;
HalPropValue getValue(HalPropValue requestedPropValue)
throws IllegalArgumentException, ServiceSpecificException {
ObjectWrapper resultWrapper = new ObjectWrapper<>();
resultWrapper.object = new ValueResult();
int status = invokeRetriable(() -> {
resultWrapper.object = internalGet(requestedPropValue);
return resultWrapper.object.status;
}, mWaitCapMs, mSleepMs);
ValueResult result = resultWrapper.object;
if (StatusCode.INVALID_ARG == status) {
throw new IllegalArgumentException(
getValueErrorMessage("get", requestedPropValue, result.errorMsg));
}
if (StatusCode.OK != status || result.propValue == null) {
if (StatusCode.OK == status) {
status = StatusCode.NOT_AVAILABLE;
}
throw new ServiceSpecificException(
status, getValueErrorMessage("get", requestedPropValue, result.errorMsg));
}
return result.propValue;
}
private ValueResult internalGet(HalPropValue requestedPropValue) {
final ValueResult result = new ValueResult();
try {
result.propValue = mVehicle.get(requestedPropValue);
result.status = StatusCode.OK;
result.errorMsg = new String();
}
...
return result;
}
...
}
internalGet() 的实现由持有的 VehicleStub 实例的 get 方法完成,其实现对应于依据 HIDL 的配置调用 HAL 侧获取相应数据。
public abstract class VehicleStub {
...
@Nullable
public abstract HalPropValue get(HalPropValue requestedPropValue)
throws RemoteException, ServiceSpecificException;
...
}
set 写入的链路和 get 大同小异,后面我会以车窗状态和开关操作为例,详细展开车辆属性 API 的使用和原理细节。
我们通过一张表格来总结 CarService 相关组件的构成。
Car 相关组件 | 所属进程 | 作用 |
---|---|---|
CarServiceHelperService | SystemServer | 管理 Car Serivce 的系统服务 |
CarService | builtin app | Car 核心服务 |
updatable app | Car 核心服务的具体实现 | |
Car API | android.car.jar | Car API SDK |
Car Apps | Launcher 等 | Car 专门设计的一系列 App |
而 CarService 在系统中的位置、与外部的交互链路,则是通过一张总图来直观把握:
CarServiceHelperService
系统服务,并交由 CarServiceHelperServiceUpdatableImpl
实际负责和 CarService
的绑定ServiceProxy
完成中转,即反射出 updatable app 中 CarServiceImpl
实例ICarImpl
实例并构建内部的一堆具体服务 CarServiceBase
,比如负责输入的 CarInputService
、负责车辆属性的 CarPropertyService
等。HalServiceBase
和 VehicleHal 进行交互,比如调度输入事件的 InputHalService
、读写/转换车辆属性的 PropertyHalService
等VehicleHal
通过 HIDL 和 HAL 层交互Car
API 获取 CarService 中的服务接口即 ICarImplCarInputManager
、读写车辆属性的 CarPropertyManager
为了帮助到大家更好入手车载,根据官方文档+行业大牛们一些指导,整理出了《Android 车载开发手册》+《Android车载开发学习手册》+《Framework 知识点汇总》,希望能帮助到大家,有需要的可以 点此处进行参考↓↓↓ 学习!!!
第一章——Android Auto概述
第二章——开发汽车媒体应用
第三章——构建 Android Auto即时通信应用
第四章——构建车载导航和地图的注点应用
第五章——构建 Android Automotive OS 视频应用
第六章——测试Android 车载应用
第七章——分发 Android 车载应用
第八章——适用于车载的Google Play服务
第九章——Android Automotive OS 通知
有需要的可以通过↓↓↓ 方式进行参考学习!!!
有需要的可以复制下方链接,传送直达!!!
https://qr21.cn/CaZQLo?BIZ=ECOMMERCE