Android Automotive(五) CarService

news/2024/5/19 6:40:52/文章来源:https://blog.csdn.net/Jun_P/article/details/127116727

Android Automotive(五) CarService

CarService是Android Automotive在系统框架层的核心服务。它类似SystemServer在服务内部管理着数十个子服务。

启动流程

CarService是由SystemServer启动的,启动流程如下。

  1. SystemServer 启动CarServiceHelperService
  2. CarServiceHelperService绑定CarService
  3. CarService创建ICarImpl实例,调用init方法
  4. ICarImpl启动其它服务

SystemServer代码:frameworks/base/services/java/com/android/server/SystemServer.java

private static final String CAR_SERVICE_HELPER_SERVICE_CLASS ="com.android.internal.car.CarServiceHelperService";
private void startOtherServices() {mActivityManagerService.systemReady(() -> {if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {traceBeginAndSlog("StartCarServiceHelperService");mSystemServiceManager.startService(CAR_SERVICE_HELPER_SERVICE_CLASS);traceEnd();}});
}

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

private static final String CAR_SERVICE_INTERFACE = "android.car.ICar";
public class CarServiceHelperService extends SystemService {@Overridepublic void onStart() {Intent intent = new Intent();intent.setPackage("com.android.car");intent.setAction(CAR_SERVICE_INTERFACE);if (!getContext().bindServiceAsUser(intent, mCarServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {Slog.wtf(TAG, "cannot start car service");}System.loadLibrary("car-framework-service-jni");}
} 
  1. SystemServerActivityManagerService.systemReady后会通过SystemServiceManager启动CarServiceHelperService
  2. CarServiceHelperService中绑定CarService

CarService清单文件:packages/services/Car/service/AndroidManifest.xml

<service android:name=".CarService"android:singleUser="true"><intent-filter><action android:name="android.car.ICar" /></intent-filter>
</service>
  1. 通过在CarServiceAndroidManifest的配置,CarServiceHelperService可以通过包名和action的名称实现和CarService绑定。

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

private ICarImpl mICarImpl;
private IVehicle mVehicle;
@Override
public void onCreate() {Log.i(CarLog.TAG_SERVICE, "Service onCreate");mCanBusErrorNotifier = new CanBusErrorNotifier(this /* context */);mVehicle = getVehicle();EventLog.writeEvent(EventLogTags.CAR_SERVICE_CREATE, mVehicle == null ? 0 : 1);Log.i(CarLog.TAG_SERVICE, "Connected to " + mVehicleInterfaceName);EventLog.writeEvent(EventLogTags.CAR_SERVICE_CONNECTED, mVehicleInterfaceName);mICarImpl = new ICarImpl(this,mVehicle,SystemInterface.Builder.defaultSystemInterface(this).build(),mCanBusErrorNotifier,mVehicleInterfaceName);mICarImpl.init();linkToDeath(mVehicle, mVehicleDeathRecipient);ServiceManager.addService("car_service", mICarImpl);SystemProperties.set("boot.car_service_created", "1");super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {return mICarImpl;
}
  1. CarService中有两个重要的变量,mICarImpl和mVehicle
  2. mICarImpl是实现ICar接口的AIDL服务端,用于和应用层通信
  3. mVehicle是实现HIDL的VehicleHal服务接口,用于同hal层通信

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

private final VehicleHal mHal;
@VisibleForTesting
ICarImpl(Context serviceContext, IVehicle vehicle, SystemInterface systemInterface,CanBusErrorNotifier errorNotifier, String vehicleInterfaceName,@Nullable CarUserService carUserService,@Nullable CarWatchdogService carWatchdogService) {mContext = serviceContext;mSystemInterface = systemInterface;mHal = new VehicleHal(serviceContext, vehicle);allServices.add(mCarWatchdogService);// Always put mCarExperimentalFeatureServiceController in last.addServiceIfNonNull(allServices, mCarExperimentalFeatureServiceController);mAllServices = allServices.toArray(new CarServiceBase[allServices.size()]);
}@MainThread
void init() {mBootTiming = new TimingsTraceLog(VHAL_TIMING_TAG, Trace.TRACE_TAG_HAL);traceBegin("VehicleHal.init");mHal.init();traceEnd();traceBegin("CarService.initAllServices");for (CarServiceBase service : mAllServices) {service.init();}traceEnd();
}
  1. ICarImpl中的第一件事就是创建VehicleHal对象,VehicleHal类是CarService中对Vehicle Service的封装,所有的和VehicleHal的交互都在这里处理。并且这里还初始化了其它hal服务。如: PowerHalService
  2. ICarImpl会创建所有的服务,然后将服务添加到CarLocalServices和mAllServices变量中,mAllServices后面在init会用到。
  3. init方法调用VehicleHalinit方法初始化mHal.init()
  4. init方法会遍历mAllServices中的服务,调用每个服务的init方法进行初始化.

架构

在这里插入图片描述

CarSevice

代码路径:packages/services/Car/service/src/com/android/car/CarService.java

CarService作为服务的入库,实际做的事情也不是很多,主要如下。

  • 创建服务进程
    这个是Android架构的特性,CarService继承了Service,可以作为一个服务在系统后台运行。

  • 创建CrashTracker
    如果Vehicle hal在10分钟内发生10次Crash,则在CarService抛出异常。

    private final CrashTracker mVhalCrashTracker = new CrashTracker(10,  // Max crash count.10 * 60 * 1000,  // 10 minutes - sliding time window.() -> {if (IS_USER_BUILD) {Log.e(CarLog.TAG_SERVICE, "Vehicle HAL keeps crashing, notifying user...");mCanBusErrorNotifier.reportFailure(CarService.this);} else {throw new RuntimeException("Vehicle HAL crashed too many times in a given time frame");}}
    )
    
  • 获取IVehicle实例
    IVehicle是HIDL的接口,通过该实例来完成和HAL层的通信

  • 完成子服务的创建,初始化子服务。

public void onCreate() {Log.i(CarLog.TAG_SERVICE, "Service onCreate");mCanBusErrorNotifier = new CanBusErrorNotifier(this /* context */);mVehicle = getVehicle();EventLog.writeEvent(EventLogTags.CAR_SERVICE_CREATE, mVehicle == null ? 0 : 1);Log.i(CarLog.TAG_SERVICE, "Connected to " + mVehicleInterfaceName);EventLog.writeEvent(EventLogTags.CAR_SERVICE_CONNECTED, mVehicleInterfaceName);mICarImpl = new ICarImpl(this,mVehicle,SystemInterface.Builder.defaultSystemInterface(this).build(),mCanBusErrorNotifier,mVehicleInterfaceName);mICarImpl.init();linkToDeath(mVehicle, mVehicleDeathRecipient);ServiceManager.addService("car_service", mICarImpl);SystemProperties.set("boot.car_service_created", "1");super.onCreate();
}
  1. mVehicle = getVehicle();获取硬件抽象层的VehicleService实例,getService相对于老版本,增加了String参数。

    private static IVehicle getVehicle() {final String instanceName = SystemProperties.get("ro.vehicle.hal", "default");try {return android.hardware.automotive.vehicle.V2_0.IVehicle.getService(instanceName);} catch (RemoteException e) {Log.e(CarLog.TAG_SERVICE, "Failed to get IVehicle/" + instanceName + " service", e);} catch (NoSuchElementException e) {Log.e(CarLog.TAG_SERVICE, "IVehicle/" + instanceName + " service not registered yet");}return null;
    }
    
  2. mICarImpl = new ICarImpl(this 创建ICarImpl对象,相当于是CarService的代理,所有的服务加载和管理都是由它来完成的。

  3. mICarImpl.init()完成子服务的初始化

  4. linkToDeath(mVehicle, mVehicleDeathRecipient);监听VehicleService的进程状态。

  5. ServiceManager.addService("car_service", mICarImpl);将自身加入到ServiceManager供其他模块获取

ICarImpl

代码路径:

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

ICarImpl实现了ICar.Stub是和应用层通信AIDL的服务端。内部完成了CarService中所有服务的加载和初始化工作。并且应用层实际也是通过此类来拿到相关服务的AIDL代理。

创建相关服务对象

ICarImpl(Context serviceContext, IVehicle vehicle, SystemInterface systemInterface,CanBusErrorNotifier errorNotifier, String vehicleInterfaceName,@Nullable CarUserService carUserService,@Nullable CarWatchdogService carWatchdogService) {mContext = serviceContext;mSystemInterface = systemInterface;mHal = new VehicleHal(serviceContext, vehicle);......mCarPropertyService = new CarPropertyService(serviceContext, mHal.getPropertyHal());......List<CarServiceBase> allServices = new ArrayList<>();allServices.add(mCarPropertyService);mAllServices = allServices.toArray(new CarServiceBase[allServices.size()]);
}

初始化

@MainThread
void init() {mBootTiming = new TimingsTraceLog(VHAL_TIMING_TAG, Trace.TRACE_TAG_HAL);traceBegin("VehicleHal.init");mHal.init();//初始化VehicleHaltraceEnd();traceBegin("CarService.initAllServices");for (CarServiceBase service : mAllServices) {service.init();//各个子service调用init}traceEnd();
}

初始化VehicleHal,以及让各个子服务调用init方法

获取子服务

getCarService获取服务代理的接口

public static final String CABIN_SERVICE = "cabin";
public static final String HVAC_SERVICE = "hvac";
public static final String INFO_SERVICE = "info";
public static final String PROPERTY_SERVICE = "property";
public static final String SENSOR_SERVICE = "sensor";
public static final String VENDOR_EXTENSION_SERVICE = "vendor_extension";
@Override
public IBinder getCarService(String serviceName) {switch (serviceName) {case Car.CABIN_SERVICE:case Car.HVAC_SERVICE:case Car.INFO_SERVICE:case Car.PROPERTY_SERVICE:case Car.SENSOR_SERVICE:case Car.VENDOR_EXTENSION_SERVICE:return mCarPropertyService;}
}

以上所有字段的接口都会返回CarPropertyService,这里除了PROPERTY_SERVICE,其它字段已经不推荐使用( @deprecated)。

CarPropertyService

管理车辆属性的服务,车辆属性通过此服务完成对系统应用层和硬件抽象层的数据通信。其中有一个重要的成员变量PropertyHalService,其具体功能通过PropertyHalService接口调用实现。CarPropertyService其功能主要是维护客户端注册的listener。CarPropertyService来负责车辆属性的管理,使上层应用更易于处理车辆属性。

CarProperty继承了ICarProperty.Stub,作为和系统应用通信的服务端

public class CarPropertyService extends ICarProperty.Stub

ICarProperty代码:packages/services/Car/car-lib/src/android/car/hardware/property/ICarProperty.aidl

package android.car.hardware.property;import android.car.hardware.CarPropertyConfig;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.property.ICarPropertyEventListener;/*** @hide*/
interface ICarProperty {void registerListener(int propId, float rate, in ICarPropertyEventListener callback) = 0;void unregisterListener(int propId, in ICarPropertyEventListener callback) = 1;List<CarPropertyConfig> getPropertyList() = 2;CarPropertyValue getProperty(int prop, int zone) = 3;void setProperty(in CarPropertyValue prop, in ICarPropertyEventListener callback) = 4;String getReadPermission(int propId) = 5;String getWritePermission(int propId) = 6;
}

重要属性说明

private final Map<IBinder, Client> mClientMap = new ConcurrentHashMap<>();
private final Map<Integer, CarPropertyConfig<?>> mConfigs = new HashMap<>();
private final PropertyHalService mHal;
private boolean mListenerIsSet = false;
private final Map<Integer, List<Client>> mPropIdClientMap = new ConcurrentHashMap<>();
private final SparseArray<SparseArray<Client>> mSetOperationClientMap = new SparseArray<>();
private final HandlerThread mHandlerThread =CarServiceUtils.getHandlerThread(getClass().getSimpleName());
private final Handler mHandler = new Handler(mHandlerThread.getLooper());
  • mCientMap 存放listenerClient对象的Map对象。Client
    维护Service的回调接口的辅助类、每一个新注册的callback都会创建一个Client对象

    private class Client implements IBinder.DeathRecipient {private final ICarPropertyEventListener mListener;private final IBinder mListenerBinder;private final SparseArray<Float> mRateMap = new SparseArray<Float>();  
    }
    
  • mConfigs 存放系统支持的所有车辆属性

  • mHal PropertyHalService实例,处理和hal交互的逻辑

  • mPropIdClientMap 用来存放注册了车辆属性的Client

  • mSetOperationClientMap 存放进行set操作的车辆属性记录

CarPropertyService::registerListener

注册一个车辆属性的监听。注册时会检测权限ICarImpl.assertPermission(mContext, mHal.getReadPermission(propId));在注册成功后立即进行一次回调mHandler.post(() -> getAndDispatchPropertyInitValue(propertyConfig, finalClient));
client.getListener().onEvent(events);

@Override
public void registerListener(int propId, float rate, ICarPropertyEventListener listener) {if (DBG) {Log.d(TAG, "registerListener: propId=0x" + toHexString(propId) + " rate=" + rate);}if (listener == null) {Log.e(TAG, "registerListener: Listener is null.");throw new IllegalArgumentException("listener cannot be null.");}IBinder listenerBinder = listener.asBinder();CarPropertyConfig propertyConfig;Client finalClient;synchronized (mLock) {propertyConfig = mConfigs.get(propId);if (propertyConfig == null) {// Do not attempt to register an invalid propIdLog.e(TAG, "registerListener:  propId is not in config list: 0x" + toHexString(propId));return;}ICarImpl.assertPermission(mContext, mHal.getReadPermission(propId));// Get or create the client for this listenerClient client = mClientMap.get(listenerBinder);if (client == null) {client = new Client(listener);}client.addProperty(propId, rate);// Insert the client into the propId --> clients mapList<Client> clients = mPropIdClientMap.get(propId);if (clients == null) {clients = new CopyOnWriteArrayList<Client>();mPropIdClientMap.put(propId, clients);}if (!clients.contains(client)) {clients.add(client);}// Set the HAL listener if necessaryif (!mListenerIsSet) {mHal.setListener(this);}// Set the new rateif (rate > mHal.getSampleRate(propId)) {mHal.subscribeProperty(propId, rate);}finalClient = client;}// propertyConfig and client are NonNull.mHandler.post(() ->getAndDispatchPropertyInitValue(propertyConfig, finalClient));
}
private void getAndDispatchPropertyInitValue(CarPropertyConfig config, Client client) {List<CarPropertyEvent> events = new LinkedList<>();int propId = config.getPropertyId();if (config.isGlobalProperty()) {CarPropertyValue value = mHal.getProperty(propId, 0);if (value != null) {CarPropertyEvent event = new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, value);events.add(event);}} else {for (int areaId : config.getAreaIds()) {CarPropertyValue value = mHal.getProperty(propId, areaId);if (value != null) {CarPropertyEvent event = new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, value);events.add(event);}}}try {client.getListener().onEvent(events);} catch (RemoteException ex) {// If we cannot send a record, its likely the connection snapped. Let the binder// death handle the situation.Log.e(TAG, "onEvent calling failed: " + ex);}
}

CarPropertyService::unregisterListener

取消注册车辆属性监听器

@Override
public void unregisterListener(int propId, ICarPropertyEventListener listener) {if (DBG) {Log.d(TAG, "unregisterListener propId=0x" + toHexString(propId));}ICarImpl.assertPermission(mContext, mHal.getReadPermission(propId));if (listener == null) {Log.e(TAG, "unregisterListener: Listener is null.");throw new IllegalArgumentException("Listener is null");}IBinder listenerBinder = listener.asBinder();synchronized (mLock) {unregisterListenerBinderLocked(propId, listenerBinder);}
}

CarPropertyService::getPropertyList

获取车辆属性列表

@Override
public List<CarPropertyConfig> getPropertyList() {List<CarPropertyConfig> returnList = new ArrayList<CarPropertyConfig>();Set<CarPropertyConfig> allConfigs;synchronized (mLock) {allConfigs = new HashSet<>(mConfigs.values());}for (CarPropertyConfig c : allConfigs) {if (ICarImpl.hasPermission(mContext, mHal.getReadPermission(c.getPropertyId()))) {// Only add properties the list if the process has permissions to read itreturnList.add(c);}}if (DBG) {Log.d(TAG, "getPropertyList returns " + returnList.size() + " configs");}return returnList;
}

CarPropertyService::getProperty

获取车辆属性

@Override
public CarPropertyValue getProperty(int prop, int zone) {synchronized (mLock) {if (mConfigs.get(prop) == null) {// Do not attempt to register an invalid propIdLog.e(TAG, "getProperty: propId is not in config list:0x" + toHexString(prop));return null;}}ICarImpl.assertPermission(mContext, mHal.getReadPermission(prop));return mHal.getProperty(prop, zone);
}

CarPropertyService::setProperty

设置车辆属性

@Override
public void setProperty(CarPropertyValue prop, ICarPropertyEventListener listener) {int propId = prop.getPropertyId();checkPropertyAccessibility(propId);// need an extra permission for writing display units properties.if (mHal.isDisplayUnitsProperty(propId)) {ICarImpl.assertPermission(mContext, Car.PERMISSION_VENDOR_EXTENSION);}mHal.setProperty(prop);IBinder listenerBinder = listener.asBinder();synchronized (mLock) {Client client = mClientMap.get(listenerBinder);if (client == null) {client = new Client(listener);}updateSetOperationRecorder(propId, prop.getAreaId(), client);}
}

CarPropertyService::onPropertyChange

处理车辆属性回调,将硬件抽象层的车辆属性变化通知给系统应用层。

@Override
public void onPropertyChange(List<CarPropertyEvent> events) {Map<IBinder, Pair<ICarPropertyEventListener, List<CarPropertyEvent>>> eventsToDispatch =new HashMap<>();for (CarPropertyEvent event : events) {int propId = event.getCarPropertyValue().getPropertyId();List<Client> clients = mPropIdClientMap.get(propId);if (clients == null) {Log.e(TAG, "onPropertyChange: no listener registered for propId=0x"+ toHexString(propId));continue;}for (Client c : clients) {IBinder listenerBinder = c.getListenerBinder();Pair<ICarPropertyEventListener, List<CarPropertyEvent>> p =eventsToDispatch.get(listenerBinder);if (p == null) {// Initialize the linked list for the listenerp = new Pair<>(c.getListener(), new LinkedList<CarPropertyEvent>());eventsToDispatch.put(listenerBinder, p);}p.second.add(event);}}// Parse the dispatch list to send eventsfor (Pair<ICarPropertyEventListener, List<CarPropertyEvent>> p: eventsToDispatch.values()) {try {p.first.onEvent(p.second);} catch (RemoteException ex) {// If we cannot send a record, its likely the connection snapped. Let binder// death handle the situation.Log.e(TAG, "onEvent calling failed: " + ex);}}
}

PropertyHalService

CarPropertyService中用来处理和硬件抽象层通信的类,主要将系统框架层和硬件抽象层的数据结构进行转换,依赖CarPropertyUtils这个工具类。

重要属性说明

private final LinkedList<CarPropertyEvent> mEventsToDispatch = new LinkedList<>();
@GuardedBy("mLock")
private final Map<Integer, CarPropertyConfig<?>> mMgrPropIdToCarPropConfig = new HashMap<>();
@GuardedBy("mLock")
private final SparseArray<VehiclePropConfig> mHalPropIdToVehiclePropConfig =new SparseArray<>();
// Only contains propId if the property Id is different in HAL and manager
private static final Map<Integer, Integer> PROPERTY_ID_HAL_TO_MANAGER = Map.of(VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS,VehiclePropertyIds.VEHICLE_SPEED_DISPLAY_UNITS);
// Only contains propId if the property Id is different in HAL and manager
private static final Map<Integer, Integer> PROPERTY_ID_MANAGER_TO_HAL = Map.of(VehiclePropertyIds.VEHICLE_SPEED_DISPLAY_UNITS,VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS);
private static final String TAG = "PropertyHalService";
private final VehicleHal mVehicleHal;
private final PropertyHalServiceIds mPropIds;@GuardedBy("mLock")
private PropertyHalListener mListener;
@GuardedBy("mLock")
private Set<Integer> mSubscribedHalPropIds;
  • mEventsToDispatch 即将上报的数据列表,这个数据会直接发给CarPropertyService上报。这个设计很棒,接收到的数据保存在列表中,可以避免数据丢失。整个列表发给上层处理,PropertyHalService又避免了对数据阻塞。

  • mProps 车辆属的配置,在takeSupportedProperties方法完成属性的增加。

  • mVehicleHal VehicleHal的实例

  • mPropIds 数据类PropertyHalServiceIds的实例,维护系统车辆属性的权限

    private final PropertyHalServiceIds mPropIds;public PropertyHalService(VehicleHal vehicleHal) {mPropIds = new PropertyHalServiceIds();
    }public String getReadPermission(int propId) {return mPropIds.getReadPermission(propId);
    }@Nullable
    public String getWritePermission(int propId) {return mPropIds.getWritePermission(propId);
    }
    
  • mSubscribedPropIds 监听车辆属性的集合,使用集合可以有效的屏蔽重复注册的监听

PropertyHalService::getProperty

获取一个车辆属性,将硬件抽象层数据结构转换成系统框架层数据结构。

public CarPropertyValue getProperty(int mgrPropId, int areaId) {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.VehiclePropValue value = mVehicleHal.get(halPropId, areaId);if (isMixedTypeProperty(halPropId)) {VehiclePropConfig propConfig;synchronized (mLock) {propConfig = mHalPropIdToVehiclePropConfig.get(halPropId);}boolean containBooleanType = propConfig.configArray.get(1) == 1;return value == null ? null : toMixedCarPropertyValue(value,mgrPropId, containBooleanType);}return value == null ? null : toCarPropertyValue(value, mgrPropId);
}
  1. isPropertySupportedInVehicle(halPropId) 判断这个车辆属性是否支持
  2. VehiclePropValue value = mVehicleHal.get(halPropId, areaId) 通过VehicleHal获取车辆属性
  3. toCarPropertyValue(value, mgrPropId)将硬件抽象层数据结构转换成系统框架层数据结构。

PropertyHalService::setProperty

设置一个车辆属性,将系统框架层数据结构转换成硬件抽象层数据结构。

public void setProperty(CarPropertyValue prop) {int halPropId = managerToHalPropId(prop.getPropertyId());if (!isPropertySupportedInVehicle(halPropId)) {throw new IllegalArgumentException("Invalid property Id : 0x"+ toHexString(prop.getPropertyId()));}VehiclePropValue halProp;if (isMixedTypeProperty(halPropId)) {// parse mixed type property value.VehiclePropConfig propConfig;synchronized (mLock) {propConfig = mHalPropIdToVehiclePropConfig.get(prop.getPropertyId());}int[] configArray = propConfig.configArray.stream().mapToInt(i->i).toArray();halProp = toMixedVehiclePropValue(prop, halPropId, configArray);} else {halProp = toVehiclePropValue(prop, halPropId);}// CarPropertyManager catches and rethrows exception, no need to handle here.mVehicleHal.set(halProp);
}
  1. int halPropId = managerToHalPropId(prop.getPropertyId())转换成硬件抽象层用的车辆属性ID
  2. toVehiclePropValue(prop, halPropId) 将系统框架层数据结构转换称硬件抽象层数据结构。
  3. mVehicleHal.set(halProp) 通过VehicleHal设置车辆属性

PropertyHalService::onHalEvents

完成VehiclePropValue(硬件抽象层数据结构)到CarPropertyValue(系统框架层数据结构)的转化,并将其封装成CarPropertyEvent数据通知给上层。

@Override
public void onHalEvents(List<VehiclePropValue> values) {PropertyHalListener listener;synchronized (mLock) {listener = mListener;}if (listener != null) {for (VehiclePropValue v : values) {if (v == null) {continue;}if (!isPropertySupportedInVehicle(v.prop)) {Log.e(TAG, "Property is not supported: 0x" + toHexString(v.prop));continue;}// Check payload if it is a userdebug build.if (Build.IS_DEBUGGABLE && !mPropIds.checkPayload(v)) {Log.e(TAG, "Drop event for property: " + v + " because it is failed "+ "in payload checking.");continue;}int mgrPropId = halToManagerPropId(v.prop);CarPropertyValue<?> propVal;if (isMixedTypeProperty(v.prop)) {// parse mixed type property value.VehiclePropConfig propConfig;synchronized (mLock) {propConfig = mHalPropIdToVehiclePropConfig.get(v.prop);}boolean containBooleanType = propConfig.configArray.get(1) == 1;propVal = toMixedCarPropertyValue(v, mgrPropId, containBooleanType);} else {propVal = toCarPropertyValue(v, mgrPropId);}CarPropertyEvent event = new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, propVal);mEventsToDispatch.add(event);}listener.onPropertyChange(mEventsToDispatch);mEventsToDispatch.clear();}
}

PropertyHalService::getPropertyList

getPropertyList 返回车辆属性的列表,其实也就是自己维护的mProps对象。也是CarPropertyService中维护的mConfigs

public Map<Integer, CarPropertyConfig<?>> getPropertyList() {if (mDbg) {Log.d(TAG, "getPropertyList");}return mProps;
}

VehicleHal

管理所有的*halservice,这些服务都是通过车辆属性来实现数据通信的,但是其所支持的车辆属性又相对独立,互不影响。现在包括的服务如下

  • PowerHalService
  • PropertyHalService
  • InputHalService
  • VmsHalService
  • DiagnosticHalService

VehicleHal继承了IVehicleCallback.Stub,是可以接收硬件抽象层的HIDL回调。

VehicleHal::init

初始化mAllProperties,从HAL层获取车辆属性配置增加到Map中。初始化所有的子服务。

public void init() {fetchAllPropConfigs();//遍历所有属性,从hal层获取将属性加到mAllProperties对象中// PropertyHalService will take most properties, so make it big enough.ArrayList<VehiclePropConfig> configsForService = new ArrayList<>(mAllServices.size());for (int i = 0; i < mAllServices.size(); i++) {HalServiceBase service = mAllServices.get(i);int[] supportedProps =  service.getAllSupportedProperties();//获取子服务支持的车辆属性configsForService.clear();synchronized (mLock) {if (supportedProps.length == 0) {//这里是PropertyHalServicefor (Integer propId : mAllProperties.keySet()) {if (service.isSupportedProperty(propId)) {VehiclePropConfig config = mAllProperties.get(propId);mPropertyHandlers.append(propId, service);configsForService.add(config);}}} else {//这里是其他for (int prop : supportedProps) {VehiclePropConfig config = mAllProperties.get(prop);if (config == null) {continue;}mPropertyHandlers.append(prop, service);configsForService.add(config);}}}service.takeProperties(configsForService);service.init();}
}
  1. fetchAllPropConfigs遍历所有属性,从硬件层获取将属性加到mAllProperties对象中

  2. getAllSupportedProperties 获取子服务支持的车辆属性

  3. PropertyHalService会执行上面的判断

    public int[] getAllSupportedProperties() {return CarServiceUtils.EMPTY_INT_ARRAY;
    }
    private static final int[] EMPTY_INT_ARRAY = new int[0];
    
  4. 当判断改属性支持支持后将属性配置到mPropertyHandlersconfigsForServicemPropertyHandlers记录车辆属性和处理的子服务,configsForService记录车辆属性的VehiclePropConfig

  5. 调用子服务的takeProperties方法,该方法确认在fw的配置是否支持,配置定义在PropertyHalServiceIds.java中,定义了车辆属性需要的系统权限。

VehicleHal::get

get方法用来获取车辆属性,实际的执行者是HalClient

public VehiclePropValue get(int propertyId, int areaId) {if (DBG) {Log.i(CarLog.TAG_HAL, "get, property: 0x" + toHexString(propertyId)+ ", areaId: 0x" + toHexString(areaId));}VehiclePropValue propValue = new VehiclePropValue();propValue.prop = propertyId;propValue.areaId = areaId;return mHalClient.getValue(propValue);
}

VehicleHal::set

set方法用来设置车辆属性,实际的执行者是HalClient

protected void set(VehiclePropValue propValue) {mHalClient.setValue(propValue);
}

VehicleHal::getAllPropConfigs

getAllPropConfigs方法用来从硬件抽象层获取所有的车辆属性配置

public Collection<VehiclePropConfig> getAllPropConfigs() {return mAllProperties.values();
}

VehicleHal::onPropertyEvent

onPropertyEvent方法用来监听硬件抽象层传来的车辆属性变化。

@Override
public void onPropertyEvent(ArrayList<VehiclePropValue> propValues) {synchronized (mLock) {for (VehiclePropValue v : propValues) {HalServiceBase service = mPropertyHandlers.get(v.prop);if(service == null) {Log.e(CarLog.TAG_HAL, "HalService not found for prop: 0x"+ toHexString(v.prop));continue;}service.getDispatchList().add(v);mServicesToDispatch.add(service);VehiclePropertyEventInfo info = mEventLog.get(v.prop);if (info == null) {info = new VehiclePropertyEventInfo(v);mEventLog.put(v.prop, info);} else {info.addNewEvent(v);}}}for (HalServiceBase s : mServicesToDispatch) {s.onHalEvents(s.getDispatchList());s.getDispatchList().clear();}mServicesToDispatch.clear();
}
  1. onPropertyEvent重写了HIDL接口IVehicleCallback 代码路径:automotive\vehicle\2.0\IVehicleCallback.hal

    package android.hardware.automotive.vehicle@2.0;interface IVehicleCallback {/*** Event callback happens whenever a variable that the API user has* subscribed to needs to be reported. This may be based purely on* threshold and frequency (a regular subscription, see subscribe call's* arguments) or when the IVehicle#set method was called and the actual* change needs to be reported.** These callbacks are chunked.** @param values that has been updated.*/oneway onPropertyEvent(vec<VehiclePropValue> propValues);/*** This method gets called if the client was subscribed to a property using* SubscribeFlags::EVENTS_FROM_ANDROID flag and IVehicle#set(...) method was called.** These events must be delivered to subscriber immediately without any* batching.** @param value Value that was set by a client.*/oneway onPropertySet(VehiclePropValue propValue);/*** Set property value is usually asynchronous operation. Thus even if* client received StatusCode::OK from the IVehicle::set(...) this* doesn't guarantee that the value was successfully propagated to the* vehicle network. If such rare event occurs this method must be called.** @param errorCode - any value from StatusCode enum.* @param property - a property where error has happened.* @param areaId - bitmask that specifies in which areas the problem has*                 occurred, must be 0 for global properties*/oneway onPropertySetError(StatusCode errorCode,int32_t propId,int32_t areaId);
    };
    
  2. 从硬件抽象层获取到的是一个ArrayList,这里会循环遍历列表中每一个车辆属性。

  3. HalServiceBase service = mPropertyHandlers.get(v.prop);这个是获取当前车辆属性需要通知的子HalService或者说是获取管理这个车辆属性的服务。

  4. service.getDispatchList().add(v) 将这个车辆属性添加到管理这个服务的分派列表中。

  5. mServicesToDispatch.add(service);将此子服务添加到服务分发列表中。

  6. s.onHalEvents(s.getDispatchList());是将前面识别到的车辆属性,让其对应的服务执行分发操作。普通车辆属性的执行者是PropertyHalService

HalClient

相当于VehicleHal的代理类,VehicleHal中定义了和硬件抽象层交互的接口,但是具体实现实际都是通过HalClient来进行调用的。HalClient中持有硬件抽象层VehicleService的句柄。

HalClient::getAllPropConfigs

获取全部的车辆属性配置

ArrayList<VehiclePropConfig> getAllPropConfigs() throws RemoteException {return mVehicle.getAllPropConfigs();
}

HalClient::subscribe

订阅一个车辆属性变化

public void subscribe(SubscribeOptions... options) throws RemoteException {mVehicle.subscribe(mInternalCallback, new ArrayList<>(Arrays.asList(options)));
}

HalClient::unsubscribe

取消一个车辆属性订阅

public void unsubscribe(int prop) throws RemoteException {mVehicle.unsubscribe(mInternalCallback, prop);
}

HalClient::setValue

设置一个车辆属性

public void setValue(VehiclePropValue propValue) {int status = invokeRetriable(() -> {try {return mVehicle.set(propValue);} catch (RemoteException e) {Log.e(TAG, getValueErrorMessage("set", propValue), e);return StatusCode.TRY_AGAIN;}}, mWaitCapMs, mSleepMs);if (StatusCode.INVALID_ARG == status) {throw new IllegalArgumentException(getValueErrorMessage("set", propValue));}if (StatusCode.OK != status) {Log.e(TAG, getPropertyErrorMessage("set", propValue, status));throw new ServiceSpecificException(status,"Failed to set property: 0x" + Integer.toHexString(propValue.prop)+ " in areaId: 0x" + Integer.toHexString(propValue.areaId));}
}

HalClient::getValue

获取一个车辆属性

VehiclePropValue getValue(VehiclePropValue requestedPropValue) {final ObjectWrapper<VehiclePropValue> valueWrapper = new ObjectWrapper<>();int status = invokeRetriable(() -> {ValueResult res = internalGet(requestedPropValue);valueWrapper.object = res.propValue;return res.status;}, mWaitCapMs, mSleepMs);if (StatusCode.INVALID_ARG == status) {throw new IllegalArgumentException(getValueErrorMessage("get", requestedPropValue));}if (StatusCode.OK != status || valueWrapper.object == null) {// If valueWrapper.object is null and status is StatusCode.Ok, change the status to be// NOT_AVAILABLE.if (StatusCode.OK == status) {status = StatusCode.NOT_AVAILABLE;}Log.e(TAG, getPropertyErrorMessage("get", requestedPropValue, status));throw new ServiceSpecificException(status,"Failed to get property: 0x" + Integer.toHexString(requestedPropValue.prop)+ " in areaId: 0x" + Integer.toHexString(requestedPropValue.areaId));}return valueWrapper.object;
}
private ValueResult internalGet(VehiclePropValue requestedPropValue) {final ValueResult result = new ValueResult();try {mVehicle.get(requestedPropValue,(status, propValue) -> {result.status = status;result.propValue = propValue;});} catch (RemoteException e) {Log.e(TAG, getValueErrorMessage("get", requestedPropValue), e);result.status = StatusCode.TRY_AGAIN;}return result;
}

CarPropertyConfig

车辆属性的属性配置,在FW层使用的,由硬件抽象层的VehiclePropConfig转化而来。定义了车辆属性的基本信息。包括读写性质、类型、变化模式等等。

private final int mAccess;
private final int mAreaType;
private final int mChangeMode;
private final ArrayList<Integer> mConfigArray;
private final String mConfigString;
private final float mMaxSampleRate;
private final float mMinSampleRate;
private final int mPropertyId;
private final SparseArray<AreaConfig<T>> mSupportedAreas;
private final Class<T> mType;

CarPropertyUtils

一个工具类,将硬件抽象层数据结构和系统框架层数据结构相互进行转换。

  • toCarPropertyValue
    转换VehiclePropValue变成CarPropertyValue。(HAL->FW)

    static CarPropertyValue<?> toCarPropertyValue(VehiclePropValue halValue, int propertyId) 
    
  • toVehiclePropValue
    转换CarPropertyValue变成VehiclePropValue。(HW->HAL)

    static VehiclePropValue toVehiclePropValue(CarPropertyValue carProp, int halPropId)
    
  • toCarPropertyConfig
    转换VehiclePropConfig变成CarPropertyConfig。(HAL->FW)

    static CarPropertyConfig<?> toCarPropertyConfig(VehiclePropConfig p, int propertyId)
    

转化关系

方法转化关系
toCarPropertyValueVehiclePropValue ->CarPropertyValue(fw)
toVehiclePropValueCarPropertyValue ->VehiclePropValue(hal)
toCarPropertyConfigVehiclePropConfig ->CarPropertyConfig(fw)

CarPropertyConfig是某一个车辆属性的信息。相当于Map中的KEY。

CarPropertyValue 是某一个车辆属性返回的值。相当于Map中的Value。

VehiclePropConfig和VehiclePropValue的定义实际上是在types.hal中。

CarPropertyValue变量的对应关系

CarPropertyValueVehiclePropValue
mPropertyId intprop int32_t
mAreaIdintareaIdint32_t
mStatusintstatusint32_t
mTimestamp longtimestamp int64_t
mValue Tvalue RawValue

系统框架层的CarPropertyValue返回值是一个泛型,可以根据属性实际的定义来决定对应的类型。硬件抽象层定义是一个数据结构,RawValue里定义了常规类型的数组和字符串。

struct RawValue {vec<int32_t> int32Values;vec<float> floatValues;vec<int64_t> int64Values;vec<uint8_t> bytes;string stringValue;
};

如果是数据类型不是数组,会占用数组的第0位返回,布尔型会用int32Values.get(0) == 1

CarPropertyConfig变量的对应关系

CarPropertyConfigVehiclePropConfig
mAccess intaccess VehiclePropertyAccess
mAreaType int
mChangeMode intchangeMode VehiclePropertyChangeMode
mConfigArray ArrayList<Integer>configArray vec<int32_t>
mConfigString StringconfigString string
mMaxSampleRate floatmaxSampleRate float
mMinSampleRate floatminSampleRate float
mPropertyId intprop int32_t
mSupportedAreas SparseArray<AreaConfig<T>> mSupportedAreasareaConfigs vec<VehicleAreaConfig>
mType Class<T>

mType是根据车辆属性的定义计算出来的,Class<?> clazz = getJavaClass(p.prop & VehiclePropertyType.MASK)

PropertyHalServiceIds

定义了PropertyHalService支持的车辆属性。此类将读写权限绑定到属性ID。提供获取车辆属性权限定义的接口
代码路径:packages/services/Car/service/src/com/android/car/hal/PropertyHalServiceIds.java

public class PropertyHalServiceIds {// Index (key is propertyId, and the value is readPermission, writePermissionprivate final SparseArray<Pair<String, String>> mProps;public PropertyHalServiceIds() {mProps = new SparseArray<>();// Add propertyId and read/write permissions// 在构造函数中绑定权限mProps.put(VehicleProperty.DOOR_POS, new Pair<>(Car.PERMISSION_CONTROL_CAR_DOORS,Car.PERMISSION_CONTROL_CAR_DOORS));//......}
}
  • getReadPermission() 获取读取权限,返回值为字符串

    public String getReadPermission(int propId) {Pair<String, String> p = mProps.get(propId);if (p != null) {// Property ID exists.  Return read permission.if (p.first == null) {Log.e(TAG, "propId is not available for reading : 0x" + toHexString(propId));}return p.first;} else if (isVendorProperty(propId)) {// if property is vendor property and do not have specific permission.return Car.PERMISSION_VENDOR_EXTENSION;} else {return null;}
    }
    
  • getWritePermission() 获取写入权限,返回值为字符串

    public String getWritePermission(int propId) {Pair<String, String> p = mProps.get(propId);if (p != null) {// Property ID exists.  Return write permission.if (p.second == null) {Log.e(TAG, "propId is not writable : 0x" + toHexString(propId));}return p.second;} else if (isVendorProperty(propId)) {// if property is vendor property and do not have specific permission.return Car.PERMISSION_VENDOR_EXTENSION;} else {return null;}
    }
    
  • isSupportedProperty() 检查车辆属性是否在PropertyHalService的已知列表中

    public boolean isSupportedProperty(int propId) {// Property is in the list of supported propertiesreturn mProps.get(propId) != null || isVendorProperty(propId);
    }
    
    private static boolean isVendorProperty(int propId) {return (propId & VehiclePropertyGroup.MASK) == VehiclePropertyGroup.VENDOR;
    }
    

总结

说明
CarServiceCar的主服务
ICarImpl具体Car的实现类
VehicleHal实现IVehicleCallback接口的类,并维护各个子服务
PropertyHalService处理HAL层获取到的数据并将其转化成FW层使用的类型
HalClient持有IVehile接口,代理实际操作
PropertyHalServiceIds定义支持的属性以及权限
CarPropertyUtils工具类,转化FW层和HAL层数据
CarPropertyConfig车辆属性配置信息
CarPropertyService车辆属性控制的子服务
Client车辆车辆属性订阅通知的类

CarService作为主服务,通过ICarImpl类实现对子服务CarPropertyService的创建以及对VehicleHal的初始化。VehicleHal中完成对子服务们的初始化,并通过传入的IVehicle对象使得PropertyHalService获取系统支持的车辆属性,完成初始化操作。在PropertyHalService中创建HalClient对象完成对IVehicle接口的实际调用,并将从HAL层获取的数据类型转化成FW层使用数据类型。CarPropertyService持有PropertyHalService的引用,完成上下层的数据交互操作。对于APPLICATION注册的Listener,CarPropertyService使用Client对象进行维护,当订阅的数据发生变化进行通知,当首次注册时,会主动获取值并进行一次通知操作。

关联关系:CarPropertyManager --> ICarProperty --> CarPropertyService --> PropertyHalService --> VehicleHal

                                     |

| --------------------- | ----------------------------------------------- |
| CarService | Car的主服务 |
| ICarImpl | 具体Car的实现类 |
| VehicleHal | 实现IVehicleCallback接口的类,并维护各个子服务 |
| PropertyHalService | 处理HAL层获取到的数据并将其转化成FW层使用的类型 |
| HalClient | 持有IVehile接口,代理实际操作 |
| PropertyHalServiceIds | 定义支持的属性以及权限 |
| CarPropertyUtils | 工具类,转化FW层和HAL层数据 |
| CarPropertyConfig | 车辆属性配置信息 |
| CarPropertyService | 车辆属性控制的子服务 |
| Client | 车辆车辆属性订阅通知的类 |

CarService作为主服务,通过ICarImpl类实现对子服务CarPropertyService的创建以及对VehicleHal的初始化。VehicleHal中完成对子服务们的初始化,并通过传入的IVehicle对象使得PropertyHalService获取系统支持的车辆属性,完成初始化操作。在PropertyHalService中创建HalClient对象完成对IVehicle接口的实际调用,并将从HAL层获取的数据类型转化成FW层使用数据类型。CarPropertyService持有PropertyHalService的引用,完成上下层的数据交互操作。对于APPLICATION注册的Listener,CarPropertyService使用Client对象进行维护,当订阅的数据发生变化进行通知,当首次注册时,会主动获取值并进行一次通知操作。

关联关系:CarPropertyManager --> ICarProperty --> CarPropertyService --> PropertyHalService --> VehicleHal

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【GNN从入门到精通】第一章 图的基本知识

文章目录一、图的表示1.1 什么是图&#xff1f;二、图的特征2.1 子图2.2 连通分量2.3 接通图2.3.1 无向图连通图2.3.2 有向连通图2.4 最短路径2.5 图直径三、图中心性3.1 度中心性3.2 特征向量中心性3.3 中介中心性3.4 连接中心性四、网页排序算法4.1 PageRank4.2 HITS4.3 例子…

游程描述的任意区域最小旋转矩形算法实现

效果 采用游程描述了多个区域,经过算法计算,找到最小包围矩形。 白色是指定的区域,黄色是该区域的最小旋转矩形。 算法原理 有时候算法不一定很高深,只要把道理点破,实现起来很简单,难的是最开始的算法构思过程。 区域最小旋转矩形的算法原理也很简单:将区域从0到90…

Dapper 在继承层次结构中处理数据

Dapper 包含一项功能,用于处理可能逐行映射到不同类型的数据。在处理使用Table Per Hierarchy存储模式的继承层次结构时,此功能特别有用,即一个表用于表示层次结构中的所有类。“鉴别器”列用于区分类型。 以下类定义表示基于抽象Contract类型的继承层次结构。还定义了三种派…

16.python实现线性单元和梯度下降-10月4日编程作业-Relu函数

目录 课堂笔记 代码实现1 运行结果1 代码实现2 评定一个学生的综合绩点 运行结果2 小结 课堂笔记 代码实现1 from cgi import print_environ from functools import reduce import numpy as np#定义感知器类 class perceptron(object):#感知器初始化函数(参数个数&…

ASP.NET Core--项目基础与搭建

文章目录项目基础与搭建创建项目ViewImports.cshtmlappsettings.json执行顺序Program.cs类startup.cs类项目基础与搭建 创建项目 ViewImports.cshtml ViewImports文件可以影响文件夹层次结构中的所有视图 ViewImports文件是我们可以编写代码并放置通用指令以引入我们的视图所需…

数据结构 - 单链表

文章目录一、单链表1.单链表的定义1.1概念介绍2.如何用代码来定义一个单链表*知识点3.单链表的插入删除未完待续...一、单链表 1.单链表的定义 单链表是一种链式存取的数据结构&#xff0c;用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的&…

Halcon快速入门笔记3

read_image (Image, ‘surface_scratch’) invert_image (Image, ImageInverted) get_image_size (ImageInverted, Width, Height) gen_sin_bandpass (ImageBandpass, 0.4, ‘none’, ‘dc_center’, Width, Height) fft_generic (ImageInverted, ImageFFT, ‘to_freq’, -…

【JavaWeb】前置知识:CSS与JavaScript知识汇总

本文被 系统学习JavaWeb 收录&#xff0c;点击订阅 写在前面 大家好&#xff0c;我是黄小黄&#xff01;经过上一篇 从Html入门JavaWeb 的学习&#xff0c;想必大家对标签都有了一定的认识。本文将通过案例&#xff0c;讲解CSS与JavaScript的基础知识&#xff0c;文章内容比较…

SSM+Vue个人健康管理系统 个人健康档案系统 个人健康信息管理系统Java

SSM+Vue个人健康管理系统 个人健康档案系统 个人健康信息管理系统Java💖🔥作者主页:计算机毕设老哥🔥 💖精彩专栏推荐订阅:在 下方专栏👇🏻👇🏻👇🏻👇🏻Java实战项目专栏 Python实战项目专栏 安卓实战项目专栏 微信小程序实战项目专栏目录Java实战项…

【数据结构从0到1】第九篇:图

文章目录一、图的基本概念二、图的存储结构2.1 邻接矩阵2.2 邻接表三、图的遍历3.1 图的广度优先遍历3.2 图的深度优先遍历四、最小生成树4.1 Kruskal算法4.2 Prim算法五、最短路径5.1 单源最短路径--Dijkstra算法5.2 单源最短路径--Bellman-Ford算法5.3 多源最短路径--Floyd-W…

基于Java开发的五子棋游戏APP设计与实现

目录 一、项目概述 1 &#xff08;一&#xff09;安卓游戏发展趋势 1 &#xff08;二&#xff09;开发安卓游戏的意义 1 二、项目需求分析 2 &#xff08;一&#xff09;功能需求分析 2 &#xff08;二&#xff09;性能需求分析 2 &#xff08;三&#xff09;可行性分析 3 &…

【点云处理】点云法向量估计及其加速(1)

点云法向量是3D点云一个极其重要的几何表面特征&#xff0c;众多的点云处理算法都依赖精确的法向量估计&#xff0c;例如点云分割&#xff0c;点云去噪等。在自动驾驶领域&#xff0c;面对特殊场景我们主要依赖点云法向量进行场景分割。估计点云法向量需要得到该点的邻域内点&a…

nodejs+vue+elementui幼儿园管理系统python java php

本设计主要分为学生家长&#xff0c;管理员和教师三个角色&#xff0c;其中学生家长功能有注册登查看幼儿信息&#xff0c;查看缴费信息&#xff0c;申请请假&#xff0c;查看体检信息等&#xff1b;教师的功能有登陆系统&#xff0c;对自己的幼儿学生&#xff0c;班级&#xf…

谷粒商城 集群篇 (二) --------- K8s 集群安装

目录一、kubeadm二、前置要求三、部署步骤四、环境准备五、安装环境1. 安装 docker① 卸载 docker② 安装 Docker-CE③ 配置 docker 加速④ 启动 docker & 设置 docker 开机自启2. 添加阿里云 yum 源3. 安装 kubeadm&#xff0c;kubelet 和 kubectl六、部署 k8s-master1. m…

STL之string

更新string&#xff0c;这段时间学习了stl&#xff0c;发现共性的东西很多&#xff0c;string&#xff0c;vector&#xff0c;list等&#xff0c;弄懂他们的逻辑框架很重要&#xff0c;其实也是stl的规范&#xff0c;文章更新内容不管这些函数怎么用&#xff0c;就从大逻辑来讲…

Java项目:SSH土地信息管理系统平台

作者主页&#xff1a;夜未央5788 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目包含管理员与用户两种角色&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录,用户管理,分类管理,档案管理等功能。 用户角色包含…

基于STM32的光敏传感器数据采集系统-物联网应用系统设计项目开发

目录1 项目概述1.1 项目介绍1.2 项目开发环境1.3 小组人员及分工2 需求分析2.1 系统需求分析2.2 可行性分析2.3 项目实施安排3 系统硬件设计3.1 系统整体硬件电路设计3.2 STM32 最小系统电路设计3.3 传感器模块电路设计3.4 光敏电阻模块电路设计4系统软件设计4.1 系统整体流程设…

源码、反码、补码和精度损失

数据类型转换,转换过程中可能导致溢出或损失精度 1.源码:源码就是二进制的数字并且开头的一位代表符号位。例:(+1)的源码:0000 0001(-1)的源码:1000 0001 2.反码:正数的反码是其本身负数的反码是符号位不懂其他位取反例: (+1)的反码:0000 0001(-1)的反码:1111 …

TC8:TCP_CLOSING_03-13

TCP_CLOSING_03: RST with DATA 目的 TCP允许接收的RST消息包含数据 测试步骤 Tester:让DUT移动到ESTABLISHED状态Tester:发送RST报文,包含一些数据DUT:不发送任何响应Tester:验证DUT在CLOSED状态期望结果 3, DUT:不发送任何响应 4, Tester:验证DUT在CLOSED状态 CANoe …

谷歌味儿

咱们从小就被教育过&#xff0c;决定一个人成败的最重要因素不是智商、暂时的能力&#xff0c;而是性格和品质。这是如此的正确&#xff0c;以至于很多公司暗暗将这个作为候选人的衡量标准。比如咱们常听到的“X里味儿”。今天咱们看看“谷歌味儿“。谷歌内部明确要求的三大品质…