万字解读 Android 车机核心 :CarService 的构成和链路~

news2024/11/17 10:50:44

前言

关于 Android 车机,之前分析过方控上自定义按键的输入机制和中控上旋钮输入的原理,但都局限于 Car Service 内 Input 相关模块。

  • 一文了解 Android 车机如何处理中控的旋钮输入
  • 从实体按键看 Android 车载的自定义事件机制

本文将结合 Android 系统整体,对 CarService 的构成和链路对其做一个全面的分析和理解。

目录

  • 构成
    1. CarServiceHelperService 系统服务
    2. CarService 核心服务 App
      • builtin app
      • updatable app
    3. Car 专用 API
    4. Car Apps
  • CarService 启动链路
    • Service 整体
    • CarInputService 输入服务
    • CarPropertyService 属性服务
  • Vehicle 属性调用链路
    • Car 获取
    • CarPropertyManager 调用
  • 总结

构成

1. CarServiceHelperService 系统服务

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.

2. CarService 核心服务 App

Car Service App 实际上分为两个,一个是和系统服务直接交互的 builtin app,另一个是给该 built app 提供实际实现的 updatable app。

builtin app

系统中与车相关的核心 App,掌管最重要的 CarService 服务类。目录位于:

  • packages/services/Car/service-builtin/

其 AndroidManifest.xml 文件如下,可以看到它具有系统权限、与 system uid 共享数据。

<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>            
</manifest>

updatable app

builtin app 的所有具体实现以及后续支持的一堆服务都在 updatable app 中实现,目录见:

  • 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.updatable"
     coreApp="true">

    <permission-group android:name="android.car.permission-group.CAR_MONITORING"
         android:icon="@drawable/perm_group_car"
         android:description="@string/car_permission_desc"
         android:label="@string/car_permission_label"/>
    ...

    <application android:label="@string/app_title"
         android:directBootAware="true"
         android:allowBackup="false">
    </application>
</manifest>

3. Car 专用 API

Android 车机里提供给系统使用汽车相关能力的专用接口,源码实现在:

  • packages/services/Car/car-lib/

看下它的 manifest 文件:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
        package="android.car" >
    <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23" />
</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:

  • android.car:包含了与车相关的基本API。例如:车辆后视镜,门,座位,窗口等

  • menu:车辆应用菜单相关API

  • cluster:仪表盘相关API

  • diagnostic:包含与汽车诊断相关的API。

  • hardware:车辆硬件相关API

  • cabin:座舱相关API

  • hvac:通风空调相关API。(hvac是Heating, ventilation and air conditioning的缩写)

  • property:属性相关API

  • radio:收音机相关API

  • input:输入相关API

  • media:多媒体相关API

  • navigation:导航相关API

  • settings:设置相关API

  • vms:汽车监测相关API

最后的实现会经由 AIDL 抵达上个章节的 CarService App。

4. Car Apps

提供 Automotive OS 内置的、专门为 Car 场景设计的 App,目录位于:

  • packages/apps/Car/

比如面向 Car 的 SystemUI、Launcher、Settings、IME 等。

CarService 启动链路

Service 整体

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<CarServiceBase> 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 相关的重要工作:

  1. 将初始化和 vehicle HAL 层交互的 VehicleHal 实例
  2. 遍历 ICarImpl 实例构造时候创建的各个扩展自 CarServiceBase 的实例,逐个调用 init() 初始化,比如:
    • 掌管车辆硬件按键输入的 CarInputService
    • 掌管车辆属性的 CarPropertyService
    • 掌管车辆电源管理的 CarPowerManagementService
    • 掌管车辆驾驶状态的 CarDrivingStateService
    • 等等
public class ICarImpl extends ICar.Stub {
    ...
    void init() {
        ...
        mHal.init();

        for (CarServiceBase service : mAllServices) {
            service.init();
        }
        ...
    }
    ...
}

下面以 CarInputService 和 CarPropertyService 为例,继续看某个具体的 CarServiceBase 初始化了什么。

CarInputService 输入服务

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 属性服务

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;
        }
    }
    ...
}

Vehicle 属性调用链路

Car 获取链路

对于其他 App 来说,想要使用 Car API,得做些准备工作:

  1. 先获取 Car 实例
  2. 然后执行连接
  3. 成功之后获取具体功能的 Manager 实例

比如这段用 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<String, CarManagerBase> 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 的话返回 CarPropertyServiceCAR_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;
        }
    }
    ...
}

CarPropertyManager 调用链路

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 <E> CarPropertyValue<E> getProperty(@NonNull Class<E> clazz, int propId, int areaId) {
        checkSupportedProperty(propId);

        try {
            CarPropertyValue<E> 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<ValueResult> 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 相关组件所属进程作用
CarServiceHelperServiceSystemServer管理 Car Serivce 的系统服务
CarServicebuiltin appCar 核心服务
updatable appCar 核心服务的具体实现
Car APIandroid.car.jarCar API SDK
Car AppsLauncher 等Car 专门设计的一系列 App

而 CarService 在系统中的位置、与外部的交互链路,则是通过一张总图来直观把握:

  1. SystemServer 进程在系统启动的时候发现 OS 具备 Automotive 的 feature,则启动 CarServiceHelperService 系统服务,并交由 CarServiceHelperServiceUpdatableImpl 实际负责和 CarService 的绑定
  2. CarService 的 builtin app 由父类 ServiceProxy 完成中转,即反射出 updatable appCarServiceImpl 实例
  3. CarServiceImpl 的初始化将构建 ICarImpl 实例并构建内部的一堆具体服务 CarServiceBase,比如负责输入的 CarInputService、负责车辆属性的 CarPropertyService 等。
  4. 这些具体服务通过 HalServiceBase 和 VehicleHal 进行交互,比如调度输入事件的 InputHalService、读写/转换车辆属性的 PropertyHalService
  5. 后续的交给 VehicleHal 通过 HIDL 和 HAL 层交互
  6. 其他 Apps 可以通过 Car lib 提供的 Car API 获取 CarService 中的服务接口即 ICarImpl
  7. ICarImpl 通过启动时候注册的服务名称和 CarServiceBase 实例对照表向 Apps 返回对应的接口实例,比如控制、监听输入的 CarInputManager、读写车辆属性的 CarPropertyManager
  8. 其他 Apps 拿到这些 Manager 接口之后,像 AOSP 中使用 ActivityManager 等接口一样通过 AIDL 和 CarService 进行交互

推荐阅读

  • 一文了解 Android 车机如何处理中控的旋钮输入
  • 从实体按键看 Android 车载的自定义事件机制
  • Android 车机初体验:Auto,Automotive 傻傻分不清楚?

参考文档 & 文章

  • https://developer.android.google.cn/reference/android/car/hardware/property/CarPropertyManager
  • https://developer.android.google.cn/reference/android/car/VehiclePropertyIds
  • https://zhuanlan.zhihu.com/p/608622393
  • https://www.jianshu.com/p/75da427d5682

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

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

相关文章

LabVIEW开发感应电机在线匝间短路故障诊断系统

LabVIEW开发感应电机在线匝间短路故障诊断系统 工业中使用的超过85%的电动机是三相感应电动机。它们因其可靠性、设计便利性、高性能和过载能力而被广泛用于不同的应用&#xff0c;例如制造、加工、电力系统、运输等。无论它们的能力如何&#xff0c;它们都被认为是现代工业学…

Consider defining a bean of type问题解决

Consider defining a bean of type问题解决 Consider defining a bean of type问题解决 包之后&#xff0c;发现项目直接报错Consider defining a bean of type。 会有一些包你明明Autowired 但是还是找不到什么bean 导致你项目启动不了 解决方法一: 这个问题主要是因为项目拆包…

安卓 MeasureCache优化了什么?

安卓绘制原理概览_油炸板蓝根的博客-CSDN博客 搜了一下&#xff0c;全网居然没有人提过 measureCache。 在前文中提到过&#xff0c;measure的时候&#xff0c;如果命中了 measureCache&#xff0c;会跳过 onMeasure&#xff0c;同时会设置 PFLAG3_MEASURE_NEEDED_BEFORE_LAYOU…

【已解决】oracle获取最近2学年的数据

已解决 &#xff1a;oracle获取最近2学年的数据 SELECT * FROM (SELECT * FROM xx.JWXT_XSKB WHERE AND xn IN (‘2023-2024’,‘2022-2023’)); 问题 某某系统课表数据过大&#xff0c;要求只获取最近2学年的数据&#xff0c;不能写死。 思路 mysql 在子查询的WHERE子句中…

从0到1学会Git(第二部分):Git的本地操作和管理

写在前面:本文介绍了在本地仓库进行文件的处理以及本地的合并等操作。 前置知识:文件可以处在三个区域&#xff0c;分别为工作区&#xff0c;暂存区和本地仓库&#xff0c;我们此文的目标即是将文件存储在本地仓库中。我们可以将文件的区域理解为&#xff0c;cpu中&#xff0c…

苍穹外卖技术栈

重难点详解 1、定义全局异常 2、ThreadLocal ThreadLocal 并不是一个Thread&#xff0c;而是Thread的一个局部变量ThreadLocal 为每一个线程提供独立的存储空间&#xff0c;具有线程隔离的效果&#xff0c;只有在线程内才能取到值&#xff0c;线程外则不能访问 public void …

linux入门---动静态库的加载

目录标题 为什么会有动态库和静态库静态库的实现动态库的实现动静态库的加载 为什么会有动态库和静态库 我们来模拟一个场景&#xff0c;首先创建两个头文件 根据文件名便可以得知add.h头文件中存放的是加法函数的声明&#xff0c;sub.h头文件中存放的是减法函数的声明&#…

【每日运维】U盘启动盘安装 ESXi 6.7.0 安装卡在 loading /bnxtroce.v00

问题描述 ● ESXi 6.7.0 安装进度卡在loading /bnxtroce.v00 进度处 处理方法 ● 重新制作启动盘&#xff0c;写入方式改为&#xff1a;【USB-ZIPv2】 ● 设置服务器的 bios设置&#xff0c;启动方式改为【UEFI】 ● 重启开机安装即可

蛋白与蛋白互作预测 蛋白互作预测protein

How to prepare structures for HADDOCK? – Bonvin Labhttps://www.bonvinlab.org/software/bpg/structures/RosettaDock: 蛋白-蛋白复合物对接预测 - 知乎 (zhihu.com) 要进行LPR1-SEPP1复合物的结合亲和力预测&#xff0c;您可以按照以下步骤进行&#xff1a; 获取蛋白质结…

MySQL的Json类型个人用法详解

前言 虽然MySQL很早就添加了Json类型&#xff0c;但是在业务开发过程中还是很少设计带这种类型的表。少不代表没有&#xff0c;当真正要对Json类型进行特定查询&#xff0c;修改&#xff0c;插入和优化等操作时&#xff0c;却感觉一下子想不起那些函数怎么使用。比如把json里的…

vue实现列表自动滚动效果

效果如图&#xff1a; 1.下载插件 npm install vue-seamless-scroll --save 2.在main.js中引入注册 import scroll from vue-seamless-scroll Vue.use(scroll) 3.在页面中使用&#xff08;写一个固定的表头 el-table:show-header"status" 设置为false,自带的表头不…

嵌入式虚拟仿真实验教学平台使用教程之搭建课程计划

嵌入式虚拟仿真实验教学平台使用教程之创建课程计划 所谓「课程计划」就是将一系列实验按照一定的顺序组织成一个教学计划&#xff0c;和传统的教学计划模式比较类似。接下来我将为大家讲解如何通过该平台创建属于自己的课程计划。 嵌入式虚拟仿真实验教学平台提供了两种创建课…

Python综合案例(基本地图使用)

一、基本地图的使用 基本代码&#xff1a; """ 演示地图可视化的基本使用 """ from pyecharts.charts import Map from pyecharts.options import VisualMapOpts# 准备地图对象 map Map() # 准备数据 data [("北京", 99),("…

js获得相对路径文件,并上传到服务器

如何通过js获得相对路径文件 已知一个相对路径文件&#xff0c;如何使用js将该文件读取为File格式&#xff0c;最后上传到服务器中呢。 1.最简单的解决方案——fetch 代码 import ./index.scss// js通过相对路径获取文件 function FetchGetLocalFile() {const fetchLocalFile …

【0904作业】QT 完成登陆界面跳转到聊天室+完成学生管理系统的查找和删除功能

一、完成登陆界面跳转到聊天室 1> 项目结构 2> 源码 ① .pro ②main #include "mywnd.h" #include"chatCli.h" #include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);MyWnd w;w.show();Form f;QObject::co…

【LeetCode算法系列题解】第51~55题

CONTENTS LeetCode 51. N 皇后&#xff08;困难&#xff09;LeetCode 52. N 皇后 II&#xff08;困难&#xff09;LeetCode 53. 最大子序和&#xff08;中等&#xff09;LeetCode 54. 螺旋矩阵&#xff08;中等&#xff09;LeetCode 55. 跳跃游戏&#xff08;中等&#xff09; …

【深入解析spring cloud gateway】07 自定义异常返回报文

Servlet的HttpResponse对象&#xff0c;返回响应报文&#xff0c;一般是这么写的&#xff0c;通过输出流直接就可以将返回报文输出。 OutputStream out response.getOutputStream(); out.write("输出的内容"); out.flush();在filter中如果发生异常&#xff08;例如…

如何使用GPT引领前沿与应用突破之GPT4科研实践技术与AI绘图

GPT对于每个科研人员已经成为不可或缺的辅助工具&#xff0c;不同的研究领域和项目具有不同的需求。例如在科研编程、绘图领域&#xff1a; 1、编程建议和示例代码: 无论你使用的编程语言是Python、R、MATLAB还是其他语言&#xff0c;都可以为你提供相关的代码示例。 2、数据可…

TypeScript_树结构-BST树

树结构 树的特点 树通常有一个根。连接着根的是树干树干到上面之后会进行分叉成树枝&#xff0c;树枝还会分又成更小的树枝在树枝的最后是叶子 树的抽象 树可以模拟生活中的很多场景&#xff0c;比如&#xff1a;公司组织架构、家谱、DOM Tree、电脑文件夹架构 优秀的哈希函…

神策数据 CJO 系列丨解密 CJO:连接体验的下一个前沿趋势

10 余年前&#xff0c;市场营销的焦点聚集在增长黑客如何利用 AARRR 模型&#xff08;获取 Acquisition、激活 Activation、留存 Retention、收入 Revenue、传播 Referral&#xff09;来推动并加速企业的生长发展。我们曾相信&#xff0c;在 AARRR 漏斗中&#xff0c;只要我们吸…