Automotive之CarService和Vehicle

news2025/2/24 7:49:01

目录

  • 前言
  • 一、CarService
    • 1.1 CarService 组成
    • 1.2 编译产物
    • 1.3 CarService的使用
      • 1.3.1 第一步:判断是否支持车载功能
      • 1.3.2 创建Car,获取 Manager
    • 1.4 CarService实现原理
      • 1.4.1 启动CarServiceHelperService服务
      • 1.4.2 绑定 CarService 服务
      • 1.4.3 CarService 初始化
      • 1.4.4 回调ServiceConnection
    • 1.5 CarService总结
  • 二、Vehicle
    • 2.1 Vehicle简介
    • 2.2 Vehicle组成
    • 2.3 编译产物
    • 2.4 Vehicle hal层数据管理
      • 2.4.1 vehicle模块代码目录
      • 2.4.2 Vehicle初始化流程
        • 2.4.2.1 VehicleService.cpp
        • 2.4.2.2 VehiclePropValue
        • 2.4.2.3 VehiclePropConfig
        • 2.4.2.4 VehiclePropertyStore
        • 2.4.2.5 EmulatedVehicleHal
        • 2.4.2.6 车辆属性的set、get、subscribe
    • 2.5 CarService如何跟Vehicle hal层通信
      • 2.5.1 VehicleHalManager
        • 2.5.1.1 获取车辆属性配置
        • 2.5.1.2 车辆属性的set、get
      • 2.5.2 CarService获取Vehicle服务
      • 2.5.3 Vehicle与VehicleEmulator的通信
  • 参考链接


前言

安卓原生Automotive,app到hal层的整体框架图如下:
在这里插入图片描述

一、CarService

CarService是一个服务层,编译出来就是类似于SystemUI这种系统级服务应用,该服务内部了定义了诸多的子服务来与系统通信,与hal层的vehicle通信。

它的的源码在 packages/services/Car 目录下面,这个目录下存在许多的工程。

1.1 CarService 组成

目录结构如下所示:

 .
 ├── 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

在Android开发中,通信模式通常遵循客户端/服务器(C/S)模式,其中客户端与服务端相互通信。每个服务都有对应的代理对象,比如ActivityManager代表ActivityManagerService(AMS),扮演客户端的角色。

CarService同样采用了C/S模式,其中Car App不会直接通过CarService的实例调用相关功能,而是通过对应的Car API完成对服务的调用。在这里,CarService充当服务端,而Car API则是客户端。

Android原生CarService包含了许多功能服务。这些服务与HAL层的VehicleHAL进行通信,通过车载总线(如CAN总线)与车身进行通讯。同时,它们也通过Car API:CarManager为应用层的Car App提供接口,让应用能够实现对车身的控制和状态的显示。
在这里插入图片描述

Car API 是包含各种 CarxxxManager 在内的 API。位于 /packages/services/Car/car-lib

在这里插入图片描述

原生的CarService业务量非常庞大,包含了许多与汽车相关的服务,主要有以下:

服务简介
CarPropertyService此类实现ICarProperty的binder接口。有助于更容易地创建处理车辆属性的多个Manager。
CarInputServiceCarInputService通过车辆HAL监控和处理输入事件
CarLocationService此服务在车辆停放时存储LocationManager中最后一个已知位置,并在车辆通电时恢复该位置。
CarMediaService管理汽车应用程序的当前活动媒体源。这与MediaSessionManager的活动会话不同,因为同一时间内车内只能有一个活动源。
CarPowerManagementService汽车电源管理服务。控制电源状态并与系统的其他部分交互以确保其自身状态。
CarProjectionService汽车投屏服务。
CarAudioService负责与汽车音响系统交互的服务。
AppFocusService应用程序焦点服务确保一次只有一个应用程序类型的实例处于活动状态。
GarageModeService车库模式。车库模式启用车内空闲时间。
InstrumentClusterService负责与汽车仪表盘交互的服务。
CarPackageManagerService汽车包管理服务。
CarUserService汽车多用户服务。在启动时管理用户。包括:创建用作驱动程序的用户。创建用作乘客的用户。首次运行时创建辅助管理员用户。切换驾驶员。
CarStorageMonitoringService提供存储监视数据(如I/O统计数据)的服务。为了接收此类数据,用户需要实现IIoStatsListener并根据此服务注册自己。
CarBluetoothService车载蓝牙服务-维护当前用户的蓝牙设备和配置文件连接。
FixedActivityService监控显示器顶部的Activity,并确保在固定模式下的Activity在崩溃或因任何原因进入后台时重新启动。此组件还监视目标包的更新,并在更新完成后重新启动它。
CarBugreportManagerServiceBug report服务
CarConfigurationService该服务将查看系统上的默认JSON配置文件并解析其结果。该服务将查找映射到R.raw.car_config的JSON文件。如果此值不存在或格式不正确,则此服务不会失败;相反,它返回各种配置的默认值。
CarDiagnosticService汽车诊断服务。工程模式会用到此服务。
CarDrivingStateService推断车辆当前驾驶状态的服务。它通过侦听CarPropertyService的相关属性来计算驾驶状态。
CarExperimentalFeatureServiceController控制与ExperimentalCarService的绑定以及实验功能的接口。
CarFeatureController控制汽车特性的部件。
CarNightService用于处理用于将车辆设置为夜间模式的事件。
CarOccupantZoneService用于实现CarOccupantZoneManagerAPI的服务。
CarTestService允许测试/模拟车辆HAL的服务。该服务直接使用车辆HAL API,因为车辆HAL模拟无论如何都需要直接访问该级别。
CarUxRestrictionsManagerService用户体验限制的服务。根据监听到的车辆当前驾驶状态,限制HMI显示。
OccupantAwarenessService一种服务,通过HAL边界监听占用者感知检测系统,并通过OccupantAwarenessManager将数据暴露给Android中的系统客户端。
SystemActivityMonitoringService监控AMS新Activity或Service启动的服务。
SystemStateControllerService系统状态控制服务。原生系统中是一个空服务,并没有实现。
CarMonitoringService监视应用程序资源使用情况的服务。
CarTrustedDeviceService汽车服务中启用受信任设备功能的部分。可信设备是一项功能,其中远程设备注册为可信设备,可以授权Android用户而不是用户输入密码或PIN。
CarUserNoticeService向用户显示初始通知UI的服务。它仅在启用设置时启动它,并根据用户的请求通知UI自行关闭。
VmsBrokerServiceVMS客户端实现,使用HAL特定消息编码将VmsPublisher/VmsSubscriber API调用代理到车辆HAL。
CarWatchdogService实现CarWatchdogManagerAPI的服务。CarWatchdogService作为汽车监控中介运行,它检查客户端的健康状况,并将结果报告给汽车监控服务器。

1.2 编译产物

功能: Car API
代码位置:/platform/packages/services/Car/car-lib
编译产物: android.car.jar

功能: CarService
代码位置: /platform/packages/services/Car/service
编译产物: CarService.apk

1.3 CarService的使用

CarService 需要通过 Car API 即各种CarxxManager 为应用层提供接口,所以开发者需要知道如何使用 Car API。

1.3.1 第一步:判断是否支持车载功能

APP 在调用 Car API 之前首先会调用 PMS 中的 hasSystemFeature() 方法判断设备是否支持车载功能

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
     .....
 }
@GuardedBy("mAvailableFeatures")
     final ArrayMap<String, FeatureInfo> mAvailableFeatures;@Override
     public boolean hasSystemFeature(String name, int version) {
         // allow instant applications
         synchronized (mAvailableFeatures) {
             final FeatureInfo feat = mAvailableFeatures.get(name);
             if (feat == null) {
                 return false;
             } else {
                 return feat.version >= version;
             }
         }
     }

mAvailableFeatures 里面的内容是通过读取/system/etc/permissions下面的 xml 文件
(对应位置

frameworks/native/data/etc 下的 XML 文件中的 feature 字段)

 <permissions>
     <!-- Feature to specify if the device is a car -->
     <feature name="android.hardware.type.automotive" />
     .....
 </permission>

frameworks/native/data/etc/android.hardware.type.automotive.xml

 <!-- These features determine that the device running android is a car. -->
 <permissions>
     <feature name="android.hardware.type.automotive" />
 </permissions>

1.3.2 创建Car,获取 Manager

Car 作为汽车平台最高等级的 API(packages/services/Car/car-lib/src/android/car/Car.java),为外界提供汽车所有服务和数据的访问

     // 创建 Car 实例
     Car carApiClient = Car.createCar(context);
     // 获取 CarHvacManager
     CarHvacManager manager = (CarHvacManager) mCarApiClient.getCarManager(Car.HVAC_SERVICE);

好了,我们现在就可以使用Car api 了!

1.4 CarService实现原理

要了解CarService的实现方式,首先需要理解其启动流程,该流程主要包括以下四个步骤:

  1. SystemServer 启动 CarServiceHelperService 服务
  2. 在调用 startService() 后,CarServiceHelperService 的 onStart() 方法通过 bindService 的方式启动 CarService(一个系统级别的 APK,位于 system/priv-app)
  3. 启动 CarService 后首先调用 onCreate(),创建 ICarImpl 对象并初始化,在此时创建了一系列 Car 相关的核心服务,并遍历 init 初始化
  4. 然后调用 onBind 将该 ICarImpl 对象返回给 CarServiceHelperService,CarServiceHelperService 在内部的一个 Binder 对象 ICarServiceHelperImpl传递给 CarService,建立双向跨进程

看一下CarService的启动时序图:
在这里插入图片描述

1.4.1 启动CarServiceHelperService服务

SystemServer会在startOtherServices()方法中让SystemServiceManager先通过反射的形式创建出StartCarServiceHelperService对象。

frameworks/base/services/java/com/android/server/SystemServer.java

 private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
     ...
     // 仅在 automotive 中启动
     if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
         t.traceBegin("StartCarServiceHelperService");
         final SystemService cshs = mSystemServiceManager
             .startService(CAR_SERVICE_HELPER_SERVICE_CLASS);
         if (cshs instanceof Dumpable) {
             mDumper.addDumpable((Dumpable) cshs);
         }
         if (cshs instanceof DevicePolicySafetyChecker) {
             dpms.setDevicePolicySafetyChecker((DevicePolicySafetyChecker) cshs);
         }
         t.traceEnd();
     }
     ...
 }

然后在SystemServiceManager中调用StartCarServiceHelperService的onStart()方法。

CarServiceHelperService是CarService的 SystemService 端的配套服务。

frameworks/base/services/core/java/com/android/server/SystemServiceManager.java

 public SystemService startService(String className) {
     final Class<SystemService> serviceClass = loadClassFromLoader(className,
             this.getClass().getClassLoader());
     return startService(serviceClass);
 }public void startService(@NonNull final SystemService service) {
     // Register it.
 mServices.add(service);
     long time = SystemClock.elapsedRealtime();
     try {
         service.onStart();
     } catch (RuntimeException ex) {
         throw new RuntimeException("Failed to start service " + service.getClass().getName()
                 + ": onStart threw an exception", ex);
     }
     warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");
 }

1.4.2 绑定 CarService 服务

frameworks/opt/car/services/src/com/android/internal/car/CarServiceHelperService.java

     private static final String CAR_SERVICE_INTERFACE = "android.car.ICar";@Override
     public void onStart() {
         EventLog.writeEvent(EventLogTags.CAR_HELPER_START);IntentFilter filter = new IntentFilter(Intent.ACTION_REBOOT);
         filter.addAction(Intent.ACTION_SHUTDOWN);
         mContext.registerReceiverForAllUsers(mShutdownEventReceiver, filter, null, null);
         mCarWatchdogDaemonHelper.addOnConnectionChangeListener(mConnectionListener);
         mCarWatchdogDaemonHelper.connect();
         Intent intent = new Intent();
         intent.setPackage("com.android.car");  // 绑定包名,设置广播仅对该包有效
         intent.setAction(CAR_SERVICE_INTERFACE);  // 绑定 action,表明想要启动能够响应设置的这个 action 的活动,并在清单文件 AndroidManifest.xml 中设置 action 属性
         // 绑定后回调
         if (!mContext.bindServiceAsUser(intent, mCarServiceConnection, Context.BIND_AUTO_CREATE,
                 mHandler, UserHandle.SYSTEM)) {
             Slogf.wtf(TAG, "cannot start car service");
         }
         loadNativeLibrary();
     }

packages/services/Car/service/AndroidManifest.xml

 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
         package="com.android.car"
         coreApp="true"
         android:sharedUserId="android.uid.system"> 
     ......
     <application android:label="@string/app_title"
          android:directBootAware="true"
          android:allowBackup="false"
          android:persistent="true"><service android:name=".CarService"
              android:singleUser="true"
              android:exported="true">
             <intent-filter>
                 <action android:name="android.car.ICar"/>
             </intent-filter>
         </service>
         ......
     </application>

sharedUserId 是系统级别的,类似 SystemUI,它编译出来同样是一个 APK 文件

/system/priv-app/CarService/CarService.apk

1.4.3 CarService 初始化

CarService进入启动时序后,会在onCreate()方法中进行一系列自身的初始化操作,步骤如下:

  1. 通过 HIDL 接口获取到 HAL 层的 IHwBinder 对象IVehicle,与 AIDL 的用法类似,必须持有 IHwBinder 对象我们才可以与 Vehicle HAL 层进行通信。
  2. 创建 ICarImpl 对象,并调用init方法,它就是ICar.aidl接口的实现类,我们需要通过它才能拿到其他的 Service 的 IBinder 对象。
  3. 将ICar.aidl的实现类添加到 ServiceManager 中。
  4. 设定 SystemProperty,将CarService设定为创建完成状态,只有包含CarService在内的所有的核心 Service 都完成初始化,才能结束开机动画并发送开机广播。

packages/services/Car/service/src/com/android/car/CarService.java

     @Override
     public void onCreate() {
         LimitedTimingsTraceLog initTiming = new LimitedTimingsTraceLog(CAR_SERVICE_INIT_TIMING_TAG,
                 Trace.TRACE_TAG_SYSTEM_SERVER, CAR_SERVICE_INIT_TIMING_MIN_DURATION_MS);
         initTiming.traceBegin("CarService.onCreate");
 ​
         initTiming.traceBegin("getVehicle");
         // 获取 hal 层的 Vehicle service
         mVehicle = getVehicle();
         initTiming.traceEnd();
         ...
         //创建 ICarImpl 实例
         mICarImpl = new ICarImpl(this,
                 mVehicle,
                 SystemInterface.Builder.defaultSystemInterface(this).build(),
                 mVehicleInterfaceName);
         //然后调用 ICarImpl 的 init 初始化方法
         mICarImpl.init();linkToDeath(mVehicle, mVehicleDeathRecipient);
         //将该 service 注册到 ServiceManager
         ServiceManager.addService("car_service", mICarImpl);
         //设置 boot.car_service_created 属性
         SystemProperties.set("boot.car_service_created", "1");super.onCreate();
 ​
         initTiming.traceEnd(); // "CarService.onCreate"
     }@Nullable
     private static IVehicle getVehicle() {
         final String instanceName = SystemProperties.get("ro.vehicle.hal", "default");try {
             //该 service 启动文件 hardware/interfaces/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-service.rc
             return android.hardware.automotive.vehicle.V2_0.IVehicle.getService(instanceName);
         } catch (RemoteException e) {
             Slog.e(CarLog.TAG_SERVICE, "Failed to get IVehicle/" + instanceName + " service", e);
         } catch (NoSuchElementException e) {
             Slog.e(CarLog.TAG_SERVICE, "IVehicle/" + instanceName + " service not registered yet");
         }
         return null;
     }

ICarImpl的实现如下:

  1. 创建各个核心服务对象
  2. 把服务对象缓存到 CarLocalServices 中,这里主要是为了方便 Service 之间的相互访问

/packages/services/Car/service/src/com/android/car/ICarImpl.java

     @VisibleForTesting
     ICarImpl(Context serviceContext, IVehicle vehicle, SystemInterface systemInterface,
             String vehicleInterfaceName,
             @Nullable CarUserService carUserService,
             @Nullable CarWatchdogService carWatchdogService,
             @Nullable ICarPowerPolicySystemNotification powerPolicyDaemon) {
         ...
         mContext = serviceContext;
         mSystemInterface = systemInterface;
         CarLocalServices.addService(SystemInterface.class, mSystemInterface);
         //创建 VehicleHal 对象
         mHal = constructWithTrace(t, VehicleHal.class,
                 () -> new VehicleHal(serviceContext, vehicle));
         ...
         // 创建核心服务对象,并缓存到 CarLocalServices
         mCarPropertyService = constructWithTrace(t, CarPropertyService.class, () -> new CarPropertyService(serviceContext, mHal.getPropertyHal()));
         mCarDrivingStateService = constructWithTrace(t, CarDrivingStateService.class,() -> new CarDrivingStateService(serviceContext, mCarPropertyService));
         mCarUXRestrictionsService = constructWithTrace(t, CarUxRestrictionsManagerService.class, () -> new CarUxRestrictionsManagerService(serviceContext, mCarDrivingStateService, mCarPropertyService));
         ...// 将创建的服务对象依次添加到一个 list 中保存起来
         List<CarServiceBase> allServices = new ArrayList<>();
         allServices.add(mFeatureController);
         allServices.add(mCarUXRestrictionsService); // mCarUserService depends on it
         allServices.add(mCarUserService);
         allServices.add(mSystemActivityMonitoringService);
         allServices.add(mCarPowerManagementService);
         allServices.add(mCarPropertyService);
         allServices.add(mCarDrivingStateService);
         allServices.add(mCarOccupantZoneService);
         addServiceIfNonNull(allServices, mOccupantAwarenessService);
         allServices.add(mCarPackageManagerService);
         allServices.add(mCarInputService);
         allServices.add(mGarageModeService);   
         ...
     }@MainThread
     void init() {
         LimitedTimingsTraceLog t = new LimitedTimingsTraceLog(CAR_SERVICE_INIT_TIMING_TAG,
                 Trace.TRACE_TAG_SYSTEM_SERVER, CAR_SERVICE_INIT_TIMING_MIN_DURATION_MS);
 ​
         t.traceBegin("ICarImpl.init");
 ​
         t.traceBegin("VHAL.init");
         mHal.init();
         t.traceEnd();
 ​
         t.traceBegin("CarService.initAllServices");
         //启动的所有服务遍历调用 init 初始化(各个都继承了 CarServiceBase)
         for (CarServiceBase service : mAllServices) {
             t.traceBegin(service.getClass().getSimpleName());
             service.init();
             t.traceEnd();
         }
         t.traceEnd(); // "CarService.initAllServices"
 ​
         t.traceEnd(); // "ICarImpl.init"
     }

然后将上面 onCreate() 创建的 mICarImpl 对象返回:

  1. onBind() 回调方法会继续传递通过 bindService() 传递来的 intent 对象(即上面的bindServiceAsUser方法)
  2. onUnbind() 会处理传递给 unbindService() 的 intent 对象。如果 service 允许绑定,onBind() 会返回客户端与服务互相联系的通信句柄

/packages/services/Car/

     @Override
     public IBinder onBind(Intent intent) {
         return mICarImpl;
     }

所以此处的 mICarImpl 会作为 IBinder 返回给CarServiceHelperService.java - bindServiceAsUser方法中的参数 mCarServiceConnection(回调)

1.4.4 回调ServiceConnection

ICarImpl 初始化完毕,会作为 IBinder 返回给CarServiceHelperService.java - bindServiceAsUser方法中绑定此服务的 mCarServiceConnection(回调)

mCarServiceConnection 初始化流程如下:

  1. 返回的 ICarImpl 被保存在了 CarServiceHelperService 的 mCarService
  2. mCarService.transact 跨进程通信,调用 ICar.aidl 中定义的第一个方法 setCarServiceHelper

/frameworks/opt/car/services/src/com/android/internal/car/CarServiceHelperService.java

 private static final String CAR_SERVICE_INTERFACE = "android.car.ICar";
 private IBinder mCarService;
 private final ICarServiceHelperImpl mHelper = new ICarServiceHelperImpl();private final ServiceConnection mCarServiceConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
             if (DBG) {
                 Slogf.d(TAG, "onServiceConnected: %s", iBinder);
             }
             handleCarServiceConnection(iBinder);
         }@Override
         public void onServiceDisconnected(ComponentName componentName) {
             handleCarServiceCrash();
         }
     };
     
     @VisibleForTesting
     void handleCarServiceConnection(IBinder iBinder) {
         synchronized (mLock) {
             if (mCarServiceBinder == iBinder) {
                 return; // already connected.
             }
             Slogf.i(TAG, "car service binder changed, was %s new: %s", mCarServiceBinder, iBinder);
             //1. 返回的 ICarImpl 被保存在了 CarServiceHelperService 的 mCarServiceBinder
             mCarServiceBinder = iBinder;
             Slogf.i(TAG, "**CarService connected**");
         }sendSetSystemServerConnectionsCall();
         ...
     }private void sendSetSystemServerConnectionsCall() {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(CAR_SERVICE_INTERFACE);
         data.writeStrongBinder(mHelper.asBinder());
         //将 ICarServiceHelperImpl 类型的对象作为数据跨进程传递
         data.writeStrongBinder(mCarServiceConnectedCallback.asBinder());
         IBinder binder;
         synchronized (mLock) {
             binder = mCarServiceBinder;
         }
         int code = IBinder.FIRST_CALL_TRANSACTION;
         try {
             //2. 跨进程传输
             //对端是 mCarService 即 ICarImpl,调用 binder 的 transact 进行跨进程通信
             //其 code 代表需要调用的对端方法,data 为携带的传输数据
             //FIRST_CALL_TRANSACTION  = 0x00000001,即调用对端 ICar.aidl 中定义的第一个方法 setCarServiceHelper
             if (VERBOSE) Slogf.v(TAG, "calling one-way binder transaction with code %d", code);
             // oneway void setSystemServerConnections(in IBinder helper, in IBinder receiver) = 0;
             binder.transact(code, data, null, Binder.FLAG_ONEWAY);
             if (VERBOSE) Slogf.v(TAG, "finished one-way binder transaction with code %d", code);
         }
         ...
     }

跨进程 setSystemServerConnections

     @Override
     public void setSystemServerConnections(IBinder helper, IBinder receiver) {
         Bundle bundle;
         try {
             EventLog.writeEvent(EventLogTags.CAR_SERVICE_SET_CAR_SERVICE_HELPER,
                     Binder.getCallingPid());
             assertCallingFromSystemProcess();
             //将 ICarServiceHelper 的代理端保存在 ICarImpl 内部 mICarServiceHelper
             ICarServiceHelper carServiceHelper = ICarServiceHelper.Stub.asInterface(helper);
             synchronized (mLock) {
                 mICarServiceHelper = carServiceHelper;
             }
             //同时也传给了 SystemInterface
             //此时他们有能力跨进程访问 CarServiceHelperService
             mSystemInterface.setCarServiceHelper(carServiceHelper);
             mCarOccupantZoneService.setCarServiceHelper(carServiceHelper);
             mCarUserService.setCarServiceHelper(carServiceHelper);
             ...
     }

1.5 CarService总结

  • CarService 是一个系统级别的服务 APK,其在开机时由 SystemServer 通过CarServiceHelperService 启动。

  • CarServiceHelperService 通过绑定服务的方式启动 CarService,启动之后创建了一个 Binder 对象 ICarImpl,并通过 onBind 返回给 system_server 进程。

  • ICarImpl 构造方法中创建了一系列和汽车相关的核心服务,并依次启动这些服务即调用各自 init 方法。

  • ICarImpl 返回给 CarServiceHelperService 之后,CarServiceHelperService 也将其内部的一个 Binder 对象(ICarServiceHelperImpl)传递到了 CarService 进程,自此 CarService 和 system_server 两个进程建立了双向 Binder 通信。


二、Vehicle

2.1 Vehicle简介

Vhal模块在车载项目主要作为信号传递、封装的角色。上承car_framework,下接mcu或者直接与aotusar通信。在项目开发过程中,会对VHAL进行一定的定制化,以适应实际需求的实现,大致流程为APP -> car_framework_vhal -> mcu/autosar。

在这里插入图片描述
APP与Car_framework之间接口是根据车身功能区设计的,如空调相关的功能是通过getCarService方法获取CarPropertyService对象,再通过调用setProperty(CarPropertyValue)方法继续往下设置,下列时序图以主动设置信号的setproperty为线索 。

在这里插入图片描述

在安卓原生源码中因为底层mcu或aotusar没有定制化,hal层的vehicle主要与上层的CarService进行通信。


2.2 Vehicle组成

代码路径

hardware/interfaces/automotive/vehicle,

这里按照以前的话讲,叫做硬件抽象层,这里简单说明文件的大致作用

  • types.hal 为VHAL层信号定义的配置文件
  • IVehicle.hal 为VHAl对外的主动调用接口,定义了Service层访问hal层的接口
  • IVehicleCallback.hal 为VHAL对外提供的接口回调,Hal层回调数据到Service层的回调接口

2.3 编译产物

功能: hidl通信接口
代码位置: /platform/hardware/interface/automotive/vehicle/2.0
Android.bp
IVehicleCallback.hal
IVehicle.hal
types.hal
编译产物:
android.hardware.automotive.vehicle-V2.0-java.jar
android.hardware.automotive.vehicle@2.0.so

功能: hal层具体实现
代码位置: /platform/hardware/interface/automotive/vehicle/2.0/default
编译产物: 可执行文件 android.hardware.automotive.vehicle@2.0-service
动态库 android.hardware.automotive.vehicle@2.0-manager-lib.so
静态库 android.hardware.automotive.vehicle@2.0-default-impl-lib.a


2.4 Vehicle hal层数据管理

2.4.1 vehicle模块代码目录

首先看下vehicle模块代码目录。
在这里插入图片描述
分析 Android.bp 文件,我们可以确定该模块下的哪些文件被编译成了哪些产物。在 Android.bp 文件中配置了编译三个产物,其中包括一个动态库。

// Vehicle reference implementation lib
cc_library {
    srcs: [
        "common/src/Obd2SensorStore.cpp",
        "common/src/SubscriptionManager.cpp",
        "common/src/VehicleHalManager.cpp",
        "common/src/VehicleObjectPool.cpp",
        "common/src/VehiclePropertyStore.cpp",
        "common/src/VehicleUtils.cpp",
        "common/src/VmsUtils.cpp",
    ],
}

以上包含的cpp文件将会被编译后将会生成名为android.hardware.automotive.vehicle@2.0-manager-lib的库文件,
暂且称之为manager-lib

接下来另外一个

// Vehicle default VehicleHAL implementation
    srcs: [
        "impl/vhal_v2_0/EmulatedVehicleHal.cpp",
        "impl/vhal_v2_0/VehicleEmulator.cpp",
        "impl/vhal_v2_0/PipeComm.cpp",
        "impl/vhal_v2_0/SocketComm.cpp",
        "impl/vhal_v2_0/LinearFakeValueGenerator.cpp",
        "impl/vhal_v2_0/JsonFakeValueGenerator.cpp",
    ],

以上几个文件将会被编译成一个名为
android.hardware.automotive.vehicle@2.0-default-impl-lib的库文件,
暂且称之为impl-lib

这两个库跟VehicleService.cpp文件最终被一起编译成
android.hardware.automotive.vehicle@2.0-service这个可执行文件,
暂且称之为service

另外提一下android.hardware.automotive.vehicle@2.0-service.rc 这个rc文件,开机的时候,init进程扫到这个文件之后,会调用其中的命令拉起service运行。

service vendor.vehicle-hal-2.0 /vendor/bin/hw/android.hardware.automotive.vehicle@2.0-service
    class hal
    user vehicle_network
    group system inet

manager-lib主要负责与空中系统的通讯,即上层功能。VehicleHalManager类集成了IVehicle接口,可实现上下通信;还包含数据缓存功能和数据订阅管理的类,以及一些工具类。

impl-lib是虚拟车身属性模块的实现,主要由Emulator组成。EmulatedVehicleHal是VehicleHal类的子类,VehicleHal这个类,是Android定义给开发者自行客制化实现hal层功能的interface.,所以这里,impl-lib是模拟车辆通信实现,适用于仿真器环境而非实际硬件环境。真正在车厂后期使用中需要真正定制化此块内容!

2.4.2 Vehicle初始化流程

2.4.2.1 VehicleService.cpp

在启动过程中,可执行模块的入口是main函数,而main函数位于VehicleService.cpp文件中,
这个文件的内容比较简单,可以看下

int main(int /* argc */, char* /* argv */ []) {
    auto store = std::make_unique<VehiclePropertyStore>();
    auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get());
    auto emulator = std::make_unique<impl::VehicleEmulator>(hal.get());
    auto service = std::make_unique<VehicleHalManager>(hal.get());
    configureRpcThreadpool(4, true /* callerWillJoin */);
    ALOGI("Registering as service...");
    status_t status = service->registerAsService();
    if (status != OK) {
        ALOGE("Unable to register vehicle service (%d)", status);
        return 1;
    }
    ALOGI("Ready");
    joinRpcThreadpool();
    return 1;
}

流程如下:

1.初始化VehiclePropertyStore,得到store指针
2.初始化EmulatedVehicleHal,得到hal指针,初始化时,将VehiclePropertyStore指针作为参数传输了EmulatedVehicleHal构造方法中
3.初始化VehicleEmulator,得到emulator指针,初始化时,将EmulatedVehicleHal指针作为参数传入VehicleEmulator的构造方法中。
4.初始化VehicleHalManager,获得service智能指针。
5.之前介绍了VehicleHalManager继承自IVehicle hidl接口,该接口在编译的时候自动生成了registerAsService方法,该方法就是将服务本身通过binder注册到hwservicemanager里面供其他进程连接。 这里不再深入介绍。

2.4.2.2 VehiclePropValue

我们一个个分析。 首先是VehiclePropertyStore, 在看它之前,首先了解几个数据结构,

/**
 * Encapsulates the property name and the associated value. It
 * is used across various API calls to set values, get values or to register for
 * events.
 */
struct VehiclePropValue {
    /** Time is elapsed nanoseconds since boot */
    int64_t timestamp;

    /**
     * Area type(s) for non-global property it must be one of the value from
     * VehicleArea* enums or 0 for global properties.
     */
    int32_t areaId;

    /** Property identifier */
    int32_t prop;

    /** Status of the property */
    VehiclePropertyStatus status;

    /**
     * Contains value for a single property. Depending on property data type of
     * this property (VehiclePropetyType) one field of this structure must be filled in.
     */
    struct RawValue {
        /**
         * This is used for properties of types VehiclePropertyType#INT
         * and VehiclePropertyType#INT_VEC
         */
        vec<int32_t> int32Values;

        /**
         * This is used for properties of types VehiclePropertyType#FLOAT
         * and VehiclePropertyType#FLOAT_VEC
         */
        vec<float> floatValues;

        /** This is used for properties of type VehiclePropertyType#INT64 */
        vec<int64_t> int64Values;

        /** This is used for properties of type VehiclePropertyType#BYTES */
        vec<uint8_t> bytes;

        /** This is used for properties of type VehiclePropertyType#STRING */
        string stringValue;
    };

    RawValue value;
};

VehiclePropValue 作为一个数据结构包含属性名称和关联的值。 它用于各种API调用,以设置值、获取值或注册事件。

2.4.2.3 VehiclePropConfig

struct VehiclePropConfig {
    /** Property identifier */
    int32_t prop;

    /**
     * Defines if the property is read or write or both.
     */
    VehiclePropertyAccess access;

    /**
     * Defines the change mode of the property.
     */
    VehiclePropertyChangeMode changeMode;

    /**
     * Contains per-area configuration.
     */
    vec<VehicleAreaConfig> areaConfigs;

    /** Contains additional configuration parameters */
    vec<int32_t> configArray;

    /**
     * Some properties may require additional information passed over this
     * string. Most properties do not need to set this.
     */
    string configString;

    /**
     * Min sample rate in Hz.
     * Must be defined for VehiclePropertyChangeMode::CONTINUOUS
     */
    float minSampleRate;

    /**
     * Must be defined for VehiclePropertyChangeMode::CONTINUOUS
     * Max sample rate in Hz.
     */
    float maxSampleRate;
};

VehiclePropConfig 是车身属性值配置。 以上两个数据结构均定义在type.hal文件中,type.hal文件是hidl定义数据结构的文件,在编译时会自动生成对应的数据结构。

2.4.2.4 VehiclePropertyStore

了解了这两个数据结构的内容之后,再看看下VehiclePropertyStore中怎么用的,首先也从数据接口开始切入,再VehiclePropertyStore.h文件中,定义了如下结构体

     struct RecordConfig {
        VehiclePropConfig propConfig;
        TokenFunction tokenFunction;
    };

    struct RecordId {
        int32_t prop;
        int32_t area;
        int64_t token;
        bool operator==(const RecordId& other) const;
        bool operator<(const RecordId& other) const;
    }

RecordConfig可以理解为属性记录配置,Record Id可以理解为属性记录id.
定义了一个PropertyMap的map表来保存属性值。

  using PropertyMap = std::map<RecordId, VehiclePropValue>;
  PropertyMap mPropertyValues;  // Sorted map of RecordId : VehiclePropValue.

定义了一个无序map来保存属性配置

 std::unordered_map<int32_t /* VehicleProperty */, RecordConfig> mConfigs;

既然知道了保存的值和位置,接下来看看store的增删查改。

  • 注册属性:
void VehiclePropertyStore::registerProperty(const VehiclePropConfig& config,
                                            VehiclePropertyStore::TokenFunction tokenFunc) {
    MuxGuard g(mLock);
    //很简单,mConfigs键值对插入key为config.prop, 值为RecordConfig, RecordConfig是个结构体,成员就是VehiclePropConfig跟一个函数指针。
    mConfigs.insert({ config.prop, RecordConfig { config, tokenFunc } });
}
  • 写入属性值:
bool VehiclePropertyStore::writeValue(const VehiclePropValue& propValue,
                                        bool updateStatus) {
    MuxGuard g(mLock);
    //首先从键值对的key集合里面查看是否当前需要写入属性值的属性id是否已经注册,如果当前属性id没有注册,则返回false,写入失败。
    if (!mConfigs.count(propValue.prop)) return false;
//查找RecordId
    RecordId recId = getRecordIdLocked(propValue);
    //根据RecordId从map中获取Value值
    VehiclePropValue* valueToUpdate = const_cast<VehiclePropValue*>(getValueOrNullLocked(recId));
    //如果当前没有保存该属性,则加入一条新的记录,否则的话,更新对应的值
    if (valueToUpdate == nullptr) {
        mPropertyValues.insert({ recId, propValue });
    } else {
        valueToUpdate->timestamp = propValue.timestamp;
        valueToUpdate->value = propValue.value;
        if (updateStatus) {
            valueToUpdate->status = propValue.status;
        }
    }
    return true;
}

属性临时存储的增删改查基本都差不多,都是操作map, 大家可以自行查阅VehiclePropertyStore.cpp这个文件。

2.4.2.5 EmulatedVehicleHal

上面介绍了属性值临时保存方式,接下来看下EmulatedVehicleHal这个类,该类继承自EmulatedVehicleHal接口,而该接口又继承自VehicleHal,上文说过,VehicleHal接口是android定义在hal层用于实现VHal相关功能的接口。 因此EmulatedVehicleHal就里就实现了类似车身数据管理的功能。

首先看下其初始化的构造函数:

EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore)
    : mPropStore(propStore),
      mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)),
      //mRecurrentTimer是一个工具类,内部维护一个线程,用来处理指定时间触发的事件,这个跟上层的Handler比较类似。
      mRecurrentTimer(
          std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this, std::placeholders::_1)),
//LinearFakeValueGenerator是一个模拟事件生成器,内部跟RecurrentTimer相互配合      mLinearFakeValueGenerator(std::make_unique<LinearFakeValueGenerator>(
          std::bind(&EmulatedVehicleHal::onFakeValueGenerated, this, std::placeholders::_1))),
          //JsonFakeValueGenerator跟LinearFakeValueGenerator类似,不过它是根据json配置产生假事件
      mJsonFakeValueGenerator(std::make_unique<JsonFakeValueGenerator>(
          std::bind(&EmulatedVehicleHal::onFakeValueGenerated, this, std::placeholders::_1))) {
          //注册DefaultConfig.h中定义的属性值
    initStaticConfig();
    for (size_t i = 0; i < arraysize(kVehicleProperties); i++) {
        mPropStore->registerProperty(kVehicleProperties[i].config);
    }
}
2.4.2.6 车辆属性的set、get、subscribe

构造函数完事之后,简单看下VHal接口中的set/get/subscribe是怎么实现的

VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::get(
        const VehiclePropValue& requestedPropValue, StatusCode* outStatus) {
        //当前我们要拿的属性值的属性ID是多少
    auto propId = requestedPropValue.prop;
    //这个pool是一个用于存储VehiclePropValue的对象池,这个跟Message的实现好像。
    auto& pool = *getValuePool();
    VehiclePropValuePtr v = nullptr;
    //这个就是根据propId来获取值了,OBD2_FREEZE_FRAME是OBD检测到故障信
    //息,OBD2_FREEZE_FRAME_INFO是故障检测到得时间戳。一般要获取OBD2_FREEZE_FRAME的数据之前,都要通过OBD2_FREEZE_FRAME_INFO获取时间戳。
    //除了这两个属性值,其他的都直接从临时的Store里面获取当前属性的状态值。
    switch (propId) {
        case OBD2_FREEZE_FRAME:
            v = pool.obtainComplex();
            *outStatus = fillObd2FreezeFrame(requestedPropValue, v.get());
            break;
        case OBD2_FREEZE_FRAME_INFO:
            v = pool.obtainComplex();
            *outStatus = fillObd2DtcInfo(v.get());
            break;
        default:
            auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue);
            if (internalPropValue != nullptr) {
                v = getValuePool()->obtain(*internalPropValue);
            }

            *outStatus = v != nullptr ? StatusCode::OK : StatusCode::INVALID_ARG;
            break;
    }

    return v;
}

分析完了get函数,接下来就是set函数了,这个函数代码有点长,主要是各种判断。

StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {
    //这个常量定义为false,是因为这个set函数是给上层调用的,Android层
    //不能够改变属性值的状态,只有车身发送了该属性值过来了,才可改变
    //属性状态,这个在下面会有体现。
    static constexpr bool shouldUpdateStatus = false;
    //这段代码用于测试的,生产一个假的数据请求事件。
    if (propValue.prop == kGenerateFakeDataControllingProperty) {
        StatusCode status = handleGenerateFakeDataRequest(propValue);
        if (status != StatusCode::OK) {
            return status;
        }
    } else if (mHvacPowerProps.count(propValue.prop)) {
        //这里是判断当前属性值是否属于空调电源开关,如果是的情况下,去拿它值,如果当前开关没开,则返回当前状态不可用,设置失败的CODE
        auto hvacPowerOn = mPropStore->readValueOrNull(
            toInt(VehicleProperty::HVAC_POWER_ON),
            (VehicleAreaSeat::ROW_1_LEFT | VehicleAreaSeat::ROW_1_RIGHT |
             VehicleAreaSeat::ROW_2_LEFT | VehicleAreaSeat::ROW_2_CENTER |
             VehicleAreaSeat::ROW_2_RIGHT));

        if (hvacPowerOn && hvacPowerOn->value.int32Values.size() == 1
                && hvacPowerOn->value.int32Values[0] == 0) {
            return StatusCode::NOT_AVAILABLE;
        }
    } else {
        // Handle property specific code
        switch (propValue.prop) {
            case OBD2_FREEZE_FRAME_CLEAR:
                return clearObd2FreezeFrames(propValue);
            case VEHICLE_MAP_SERVICE:
                // Placeholder for future implementation of VMS property in the default hal. For
                // now, just returns OK; otherwise, hal clients crash with property not supported.
                return StatusCode::OK;
            case AP_POWER_STATE_REPORT:
                // This property has different behavior between get/set.  When it is set, the value
                //  goes to the vehicle but is NOT updated in the property store back to Android.
                // Commented out for now, because it may mess up automated testing that use the
                //  emulator interface.
                // getEmulatorOrDie()->doSetValueFromClient(propValue);
                return StatusCode::OK;
        }
    }
	//status默认值为AVAILABLE
    if (propValue.status != VehiclePropertyStatus::AVAILABLE) {
        // Android side cannot set property status - this value is the
        // purview of the HAL implementation to reflect the state of
        // its underlying hardware
        return StatusCode::INVALID_ARG;
    }
    //读取该属性值id的当前存储的Prop
    auto currentPropValue = mPropStore->readValueOrNull(propValue);

    if (currentPropValue == nullptr) {
        return StatusCode::INVALID_ARG;
    }
    //如果目前属性值状态不可用,则上层不能设置,返回失败
    if (currentPropValue->status != VehiclePropertyStatus::AVAILABLE) {
        // do not allow Android side to set() a disabled/error property
        return StatusCode::NOT_AVAILABLE;
    }
	//更新属性值,开头说过,shouldUpdateStatus为false, 也就是android层更新属性值,不改变属性状态
    if (!mPropStore->writeValue(propValue, shouldUpdateStatus)) {
        return StatusCode::INVALID_ARG;
    }
    //通知汽车,设置属性值,这里是告诉模拟器,该值需要重新设置,调用的这个函数等下再说。
    getEmulatorOrDie()->doSetValueFromClient(propValue);
    return StatusCode::OK;
}

通过set函数了解到,属性值的设置,先将属性值写入到内存中保存,然后再通知车身更新该属性值,doSetValueFromClient这个函数就实现了相关的功能。 这个set事件是来自上层service的调用,那车身信息如果发生变化时,如何set呢,答案是该模块还有一个名称setPropertyFromVehicle的函数,正是这个函数实现了车身数据变化之后,更新缓存的属性,并通知上层。

bool EmulatedVehicleHal::setPropertyFromVehicle(const VehiclePropValue& propValue) {
    static constexpr bool shouldUpdateStatus = true;
    if (propValue.prop == kGenerateFakeDataControllingProperty) {
        StatusCode status = handleGenerateFakeDataRequest(propValue);
        if (status != StatusCode::OK) {
            return false;
        }
    }
    //更新属性值,注意这个shouldUpdateStaus为true,也就是要更新属性的status, 
    //刚刚上面那个set函数该值为false,这是为啥? 因为属性值只有由车身改变的时候才能改变其状态值,android层不行。
    if (mPropStore->writeValue(propValue, shouldUpdateStatus)) {
        //触发回调,通知上层
        doHalEvent(getValuePool()->obtain(propValue));
        return true;
    } else {
        return false;
    }
}

看下该模块的属性值订阅实现:

StatusCode EmulatedVehicleHal::subscribe(int32_t property, float sampleRate) {
    ALOGI("%s propId: 0x%x, sampleRate: %f", __func__, property, sampleRate);

    if (isContinuousProperty(property)) {
        mRecurrentTimer.registerRecurrentEvent(hertzToNanoseconds(sampleRate), property);
    }
    return StatusCode::OK;
}

传输的参数有两个 property是属性ID,

  • sampleRate是属性值更新的频率。
  • isContinuousProperty主要是判断该属性值的change类型是不是连续类型的,如果是连续类型的,就向RecurrentTimer中注册事件。
  • RecurrentTimer是一个工具类,可以把它理解为一个另类的Handler, 其内部运行着一个线程维护着一个循环,当向其注册一个事件时,内部根据事件频率算出触发事件的事件,然后定期触发回调方法,跟Handler唯一不同的是,Handler的sendMesssageAtTime发完就没了, 这个RecurrentTimer是如果你注册了事件,如果不取消注册,则事件会一直定期触发。

这里说了RecurrentTimer有个触发回调,那我们订阅了一个属性id,当达到时间后,触发的是哪个回调呢? 当然是EmulatedVehicleHal中的onContinuousPropertyTimer函数啦,这个函数指针在EmulatedVehicleHal初始化的时候,就作为参数传给RecurrentTimer,然后在这个函数中调用 doHalEvent(std::move(v)); 触发回调事件,将属性值上报。 doHalEvent中其实没做啥,可以看下它的代码

    void doHalEvent(VehiclePropValuePtr v) {
        mOnHalEvent(std::move(v));
    }

这里mOnHalEvent是一个函数指针,其对应函数定义在VehicleHalManager中,如下

void VehicleHalManager::onHalEvent(VehiclePropValuePtr v) {
    mEventQueue.push(std::move(v));
}

最终由BatchingConsumer取出该事件,回调给上层; mOnHalEvent函数指针在VehicleHalManager初始化的时候,会将其作为参数传给EmulatedVehicleHal
在这里插入图片描述
hal层数据管理讲的差不多了


2.5 CarService如何跟Vehicle hal层通信

CarService跟Vehicle hal层通信的桥梁是IVehicle接口的实现VehicleHalManager,先看下IVehicle.hal通过hidl-gen生成的接口文件中定义了哪些方法, 大概摘录了其中主要的一些方法定义,如下:
在这里插入图片描述

2.5.1 VehicleHalManager

2.5.1.1 获取车辆属性配置

getAllPropConfigs作用是获取所有的属性配置, 对应实现在VehicleHalManager中的getlAllPropConfigs方法,代码如下:

  • getlAllPropConfigs
Return<void> VehicleHalManager::getAllPropConfigs(getAllPropConfigs_cb _hidl_cb) {
    ALOGI("getAllPropConfigs called");
    //_hidl_cb是一个函数指针,定义在IVehicle.h里面
    hidl_vec<VehiclePropConfig> hidlConfigs;
    //从vector集合中读取所有当前的属性配置数据
    auto& halConfig = mConfigIndex->getAllConfigs();
    //写入到集合中
    hidlConfigs.setToExternal(
            const_cast<VehiclePropConfig *>(halConfig.data()),
            halConfig.size());
     //回调将数据发送到上层
    _hidl_cb(hidlConfigs);

    return Void();
}

上面这个函数是上层获取当前hal层支持的所有属性配置,接下来看根据属性id获取属性配置,对应的函数为getPropConfigs,代码如下:

  • getPropConfigs
Return<void> VehicleHalManager::getPropConfigs(const hidl_vec<int32_t> &properties,
                                               getPropConfigs_cb _hidl_cb) {
    //这个函数也比较简单,基本上就是判断当前属性id是否已经默认配置了,如果存在,则保存配置到集合中,否则的话,就针对未配置的属性id上报状态错误的消息。
    std::vector<VehiclePropConfig> configs;
    for (size_t i = 0; i < properties.size(); i++) {
        auto prop = properties[i];
        if (mConfigIndex->hasConfig(prop)) {
            configs.push_back(mConfigIndex->getConfig(prop));
        } else {
            ALOGW("Requested config for undefined property: 0x%x", prop);
            _hidl_cb(StatusCode::INVALID_ARG, hidl_vec<VehiclePropConfig>());
        }
    }

    _hidl_cb(StatusCode::OK, configs);

    return Void();
}
2.5.1.2 车辆属性的set、get

上面两个是关于属性配置获取的,属性配置跟属性值的区别是啥,一个是描述你这个配置的一些行为,如访问权限,更新最大的频率等; 一个是用于具聚合一些具体值信息的载体,下面看一下属性值的设置跟获取,set/get函数。

  • set函数
//这个函数是上层调用set设置属性值的时候,参数value来自上层
Return<StatusCode> VehicleHalManager::set(const VehiclePropValue &value) {
    auto prop = value.prop;
    //获取属性配置
    const auto* config = getPropConfigOrNull(prop);
    //如果获取不到这个属性ID的配置,就证明当前hal层没配这个属性值,也就是不支持它
    if (config == nullptr) {
        ALOGE("Failed to set value: config not found, property: 0x%x", prop);
        return StatusCode::INVALID_ARG;
    }
//检查权限,上面说了,属性配置里面有关于访问权限的值定义,如果该属性值定义的配置不支持写权限,那就返回失败
    if (!checkWritePermission(*config)) {
        return StatusCode::ACCESS_DENIED;
    }
   //告诉上层订阅了该属性id的监听器,该值有更新。
    handlePropertySetEvent(value);
	//这里就到了上面介绍EmulatorVehicleHal中的set函数
    auto status = mHal->set(value);

    return Return<StatusCode>(status);
}

get函数,获取属性值

  • get
Return<void> VehicleHalManager::get(const VehiclePropValue& requestedPropValue, get_cb _hidl_cb) {
//逻辑跟set差不读,先获取属性配置,然后再检查权限,最后从缓存中取值。
    const auto* config = getPropConfigOrNull(requestedPropValue.prop);
    if (config == nullptr) {
        ALOGE("Failed to get value: config not found, property: 0x%x",
              requestedPropValue.prop);
        _hidl_cb(StatusCode::INVALID_ARG, kEmptyValue);
        return Void();
    }

    if (!checkReadPermission(*config)) {
        _hidl_cb(StatusCode::ACCESS_DENIED, kEmptyValue);
        return Void();
    }

    StatusCode status;
    auto value = mHal->get(requestedPropValue, &status);
    _hidl_cb(status, value.get() ? *value : kEmptyValue);
    return Void();
}

get函数跟set函数最终都是EmulatorVehicleHal.cpp中去执行具体的数据更新。

VHal中的VehicleHalManager跟上层进行数据通讯,包括数据回调,跟上层调用,都均由此实现。

EmulatorVehicleHal模块承接了VehicleHalManager中的一部分属性值设置获取的功能,当上层设置值的时候,EmulatorVehicleHal会去更新内存中缓存的值,然后通知车身; 当车身有数据更新时,也会该模块更新缓存值,并触发回调,通知上层。


2.5.2 CarService获取Vehicle服务

vhal使用了hidl接口,最终会编译出一个jar包, 生成了
android.hardware.automotive.vehicle-V2.0-java.jar被service编译的时候依赖了,其中自动生成的IVehicle接口中就实现了Service与Vhal进行binder通信的逻辑,这部分不需要太关心,
只需要知道拿到IVehicle接口实例就能通信就OK了, 那怎么拿到该实例呢,IVehicle中有一个这样的方法!

android.hardware.automotive.vehicle.V2_0.IVehicle.getService()

这是一个静态方法,CarService正是通过该方法拿到IVehicle的实例

    @Nullable
    private static IVehicle getVehicle() {
        try {
            return android.hardware.automotive.vehicle.V2_0.IVehicle.getService();
        } catch (RemoteException e) {
            Log.e(CarLog.TAG_SERVICE, "Failed to get IVehicle service", e);
        } catch (NoSuchElementException e) {
            Log.e(CarLog.TAG_SERVICE, "IVehicle service not registered yet");
        }
        return null;
    }

具体流程看上面CarService的初始化便可以看到。

车身信息这类属性模块对应的管理模块就是CarPropertyService。 这里简单介绍下服务数据是如何下发的,也就是CarPropertyService中setProperty的后续处理逻辑。

    public void setProperty(CarPropertyValue prop) {
       //查找该属性是否未有效的属性
        int halPropId = managerToHalPropId(prop.getPropertyId());
	Log.d(TAG, "PropertyHalService setProperty halPropId = " + halPropId);
        if (halPropId == NOT_SUPPORTED_PROPERTY) {
            throw new IllegalArgumentException("Invalid property Id : 0x"
                    + toHexString(prop.getPropertyId()));
        }
        //将CarPropertyValue转换为VehiclePropValue对象,注意这个VehiclePropValue是hidl自动生成的,包含在jar包中,下发该对象后,vhal模块能够直接取值。
        VehiclePropValue halProp = toVehiclePropValue(prop, halPropId);
        try {
            mVehicleHal.set(halProp);
        } catch (PropertyTimeoutException e) {
            Log.e(CarLog.TAG_PROPERTY, "set, property not ready 0x" + toHexString(halPropId), e);
            throw new RuntimeException(e);
        }
    }

最后一路set到IVehicle中,将数据发送给vhal

 public void setValue(VehiclePropValue propValue) throws PropertyTimeoutException {
        int status = invokeRetriable(() -> {
            try {
               //此处将VehiclePropValue下发到VHAL模块
                return mVehicle.set(propValue);
            } catch (RemoteException e) {
                Log.e(CarLog.TAG_HAL, "Failed to set value", e);
                return StatusCode.TRY_AGAIN;
            }
        }, WAIT_CAP_FOR_RETRIABLE_RESULT_MS, SLEEP_BETWEEN_RETRIABLE_INVOKES_MS);

//vhal上报,该参数无效
        if (StatusCode.INVALID_ARG == status) {
            throw new IllegalArgumentException(
                    String.format("Failed to set value for: 0x%x, areaId: 0x%x",
                            propValue.prop, propValue.areaId));
        }

//错误码:设置失败,请重试
        if (StatusCode.TRY_AGAIN == status) {
            throw new PropertyTimeoutException(propValue.prop);
        }

        if (StatusCode.OK != status) {
            throw new IllegalStateException(
                    String.format("Failed to set property: 0x%x, areaId: 0x%x, "
                            + "code: %d", propValue.prop, propValue.areaId, status));
        }
    }

至此,车辆属性的set结束,read流程与set差不多,此处不再赘述。

2.5.3 Vehicle与VehicleEmulator的通信

VehicleEmulator这里简单总结下,这个通讯实现比较简单,主要是运用Pipe管道或者socket通讯的方式,跟模拟器之间收发通过protobuf封装的数据,模块内部实现了protobuf数据的解析与封装,用来触发设置,获取属性值的事件等。

实际项目不可能如此处理,下面结合同事以前的实际项目经验在原生基础上做出一些补充:

在原生基础上对VHAL修改比较大的环节在一头一尾

  • 一头指的是信号的维护,这里延续原生信号的维护流程,在types.hal中添加信号。由于开发工作基于平台开发,实际维护信号数量大于运行信号,需要对信号分段维护。
  • 一尾则是在VHAL阶段处理完信号之后需要转发给mcu,这里涉及与MCU的通信方式。

这里给出两种通信方式建议:

  • 一种是FDBus+protobuf,由于FDBus底层是socket通信,所以满足socket通信的特点,易于移植与拓展,这种方式优点明显,缺点也明显,信号传递过程中需要正反序列化,传递信号相对耗时,protobuf文件维护成本高,不支持java调用,使用需要借助JNI。
    这样的框架应对Android一对多的场景优势明显,如实际场景中Android既要给MCU发消息控制车窗等车身功能,又要给仪表系统(如QNX)发送控制消息(如小计里程清零等)甚至是副驾屏幕的MCU系统发送消息。在这样的场景中FDBus+protobuf就能最大限度发挥优势
    在这里插入图片描述
  • 另一种通信方式为SPI消息,SPI消息为总线消息,递信号稳定,支持全双工,效率高,属于万金油的通信方式,缺点在于不能一对多,实际开发中需要MCU承担部分信号转发的工作,由于android与MCU或者仪表的通信方式为域内通信,不能长距离通信的缺点不做关注。
    在这里插入图片描述

发送到 CAN 总线上的信号需要遵寻 Autosar 规范并且需要与车厂共同制定,大家了解即可。


参考链接

Android 车载应用开发指南(4)- CarService 详解
Android 9.0 AutoMotive模块之CarService
Android 9.0 AutoMotive模块之Vehicle
AndroidAutomotive模块介绍(四)VehicleHal介绍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1899158.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

swagger的接口文档导入到yapi上

一、访问swagger接口 swagger集成到项目后&#xff0c;通过http:\\ip:port/swagger-ui.html 访问。 说明&#xff1a;这里的路径是基于swagger2。如果用swagger3&#xff0c;需要用swagger3的路径进行访问。 访问如图&#xff1a; 这就是swagger接口首页。如果想导入到yapi上…

PD虚拟机怎么联网?PD虚拟机安装Win11无法上网 pd虚拟机连不上网怎么解决 mac安装windows虚拟机教程

PD虚拟机既可以联网使用&#xff0c;也可以单机使用。如需将PD虚拟机联网&#xff0c;可以共享Mac原生系统的网络&#xff0c;其使用体验与真实系统无异。本文会详细讲解PD虚拟机如何联网&#xff0c;并会进一步解决PD虚拟机安装Win10无法上网的问题。 如果有网络相关问题的小伙…

SQL 与 NoSQL 数据库:一场关于灵活性与结构的对话

文章目录 引言SQL 数据库&#xff1a;传统之光定义特征优势缺点 NoSQL 数据库&#xff1a;新时代的弹性定义特征优势缺点 何时选择 NoSQL&#xff1f;场景1&#xff1a;海量数据与高并发场景2&#xff1a;灵活性需求场景3&#xff1a;实时数据分析场景4&#xff1a;分布式系统 …

无人机水运应用场景

航行运输 通航管理&#xff08;海事通航管理处&#xff09; 配员核查流程 海事员通过VHF&#xff08;甚高频&#xff09;系统与船长沟通核查时间。 无人机根据AIS&#xff08;船舶自动识别系统&#xff09;报告的船舶位置&#xff0c;利用打点定位 功能飞抵船舶上方。 使用…

TikTok马来西亚直播网络怎么配置?

TikTok是一款全球流行的社交媒体应用&#xff0c;在东南亚地区拥有大量用户。在马来西亚这个多元化的国家&#xff0c;配置高效稳定的直播网络对TikTok的运营至关重要。 配置马来西亚直播网络的必要性 广泛的地理覆盖&#xff1a;马来西亚包括大片陆地和众多岛屿&#xff0c;网…

求 自然对数 ln(x)

np.log()函数是用来计算数组中每个元素的自然对数的。自然对数是以数学常数e&#xff08;约等于2.71828&#xff09;为底的对数。NumPy作为一个强大的数值计算库&#xff0c;提供了很多用于数组操作的函数&#xff0c;np.log()就是其中之一。 • 下面是一个简单的例子&#xff…

某某市信息科技学业水平测试软件打开加载失败逆向分析(笔记)

引言&#xff1a;笔者在工作过程中&#xff0c;用户上报某某市信息科技学业水平测试软件在云电脑上打开初始化的情况下出现了加载和绑定机器失败的问题。一般情况下&#xff0c;在实体机上用户进行登录后&#xff0c;用户的账号信息跟主机的机器码进行绑定然后保存到配置文件&a…

RNN文献综述

循环神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;是一种专门用于处理序列数据的神经网络模型。它在自然语言处理、语音识别、时间序列预测等领域有着广泛的应用。本文将从RNN的历史发展、基本原理、应用场景以及最新研究进展等方面进行综述。 历…

阿里云RDS云数据库库表恢复操作

最近数据库中数据被人误删了,记录一下恢复操作方便以后发生时进行恢复. 1.打开控制台&#xff0c;进入云数据库实例. 2.进入实例后 &#xff0c;点击右侧的备份恢复&#xff0c;然后看一下备份时间点&#xff0c;中间这边都是阿里云自动备份的备份集&#xff0c;基本都是7天一备…

免密ssh和自定义服务器名字【远程连接服务器】

免密ssh和自定义服务器名字【远程连接服务器】 免密ssh和自定义服务器名字【远程连接服务器】服务器添加本地公钥ssh-copy-id使用别名登录config 免密ssh和自定义服务器名字【远程连接服务器】 原理 实现免密登录需要 本地的公钥id_rsa.pub放在服务器上的 authorized_keys 文件…

实战演练:Fail2Ban部署全攻略,确保您的服务器免受CVE-2024-6387侵害!

Fail2Ban是一个开源的入侵防护软件&#xff0c;它可以扫描日志文件&#xff0c;识别恶意行为&#xff08;如多次失败的登录尝试&#xff09;&#xff0c;并自动采取措施&#xff08;如更新防火墙规则&#xff09;来阻止攻击者。最近&#xff0c;CVE-2024-6387漏洞的爆出使我们更…

第一次的pentest show总结

第一次的pentest show总结 前言 开始之前&#xff0c;我特别感谢TryHackMe(英)、HackTheBox(美)、zero-point security(英)、offsec(美)等平台&#xff0c;使我们能够通过网络以线上的方式学习与练习&#xff0c;打破传统线下各地区教育资源差异大的限制&#xff0c;对网络教…

14-6 小型语言模型在商业应用中的使用指南

人工智能 (AI) 在商业领域的发展使众多工具和技术成为人们关注的焦点&#xff0c;其中之一就是语言模型。这些大小和复杂程度各异的模型为增强业务运营、客户互动和内容生成开辟了新途径。本指南重点介绍小型语言模型、它们的优势、实际用例以及企业如何有效利用它们。 基础知识…

01 企业网站架构部署于优化之Web基础与HTTP协议

目录 1.1 Web基础 1.1.1 域名和DNS 1. 域名的概念 2. Hosts文件 3. DNS 4. 域名注册 1.1.2 网页与HTML 1. 网页概述 2. HTML概述 3. HTML基本标签 4. 网站和主页 5. Web1.0与Web2.0 1.1.3 静态网页与动态网页 1. 静态网页 2. 动态网页 3. 动态网页语言 1.2 HTTP协议 1…

搭建vue3+vite+pinia项目步骤

方法一&#xff1a;使用vite生成项目&#xff08;确保你的 node 版本是16.0.0或更高版本&#xff09; Vite 是一个新型的前端构建工具&#xff0c;专为现代前端开发优化。 第一步&#xff1a;创建项目&#xff0c;命令如下&#xff1a; // 创建项目的命令 npm create vitela…

vue项目打包部署后 浏览器自动清除缓存问题(解决方法)

vue打包部署后 浏览器缓存问题&#xff0c;导致控制台报错ChunkLoadError: Loading chunk failed的解决方案 一、报错如下&#xff1a; 每次build打包部署到服务器上时&#xff0c;偶尔会出现前端资源文件不能及时更新到最新&#xff0c;浏览器存在缓存问题&#xff0c;这时在…

JavaSE 面向对象程序设计进阶 Lambda表达式 2024年详解

Lambda表达式 作用 简化匿名内部类的书写 排序包装类数组 改写匿名内部类 代码实现 import java.util.Arrays; import java.util.Comparator;public class Main {public static void main(String[] args) {Integer[] arrnew Integer[]{2,1,3,4};Arrays.sort(arr,(Integer o1…

大模型成为软件和数据工程师

前言 想象一下这样一个世界&#xff1a;人工智能伙伴负责编码工作&#xff0c;让软件和数据工程师释放他们的创造天赋来应对未来的技术挑战&#xff01; 想象一下&#xff1a;你是一名软件工程师&#xff0c;埋头于堆积如山的代码中&#xff0c;淹没在无数的错误中&#xff0…

PyCharm

一、介绍 PyCharm 是 JetBrains 公司开发的一款功能强大的 Python 集成开发环境&#xff08;IDE&#xff09;。它专为 Python 开发设计&#xff0c;提供了一系列强大的工具和功能&#xff0c;帮助开发者更高效地编写、调试和维护 Python 代码。以下是对 PyCharm 的详细介绍&am…

spring boot 接口参数解密和返回值加密

spring boot 接口参数解密和返回值加密 开发背景简介安装配置yml 方式Bean 方式 试一下启动项目返回值加密参数解密body 参数解密param和form-data参数解密 总结 开发背景 虽然使用 HTTPS 已经可以基本保证传输数据的安全性&#xff0c;但是很多国企、医疗、股票项目等仍然要求…