blob: 5de5a81ec961d3f4c37b2672cd8097bb73ba9a49 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car.hal;
import static com.android.car.CarServiceUtils.toByteArray;
import static com.android.car.CarServiceUtils.toFloatArray;
import static com.android.car.CarServiceUtils.toIntArray;
import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
import static java.lang.Integer.toHexString;
import android.annotation.CheckResult;
import android.annotation.Nullable;
import android.car.VehiclePropertyIds;
import android.car.hardware.property.CarPropertyManager;
import android.content.Context;
import android.hardware.automotive.vehicle.V2_0.IVehicle;
import android.hardware.automotive.vehicle.V2_0.IVehicleCallback;
import android.hardware.automotive.vehicle.V2_0.SubscribeFlags;
import android.hardware.automotive.vehicle.V2_0.SubscribeOptions;
import android.hardware.automotive.vehicle.V2_0.VehicleAreaConfig;
import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
import android.os.HandlerThread;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import com.android.car.CarLog;
import com.android.car.CarServiceUtils;
import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.google.android.collect.Lists;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* Abstraction for vehicle HAL. This class handles interface with native HAL and do basic parsing
* of received data (type check). Then each event is sent to corresponding {@link HalServiceBase}
* implementation. It is responsibility of {@link HalServiceBase} to convert data to corresponding
* Car*Service for Car*Manager API.
*/
public class VehicleHal extends IVehicleCallback.Stub {
private static final boolean DBG = false;
/**
* Used in {@link VehicleHal#dumpVehiclePropValue} method when copying {@link VehiclePropValue}.
*/
private static final int MAX_BYTE_SIZE = 20;
public static final int NO_AREA = -1;
public static final float NO_SAMPLE_RATE = -1;
private final HandlerThread mHandlerThread = CarServiceUtils.getHandlerThread(
VehicleHal.class.getSimpleName());
private final PowerHalService mPowerHal;
private final PropertyHalService mPropertyHal;
private final InputHalService mInputHal;
private final VmsHalService mVmsHal;
private final UserHalService mUserHal;
private final DiagnosticHalService mDiagnosticHal;
private final ClusterHalService mClusterHalService;
private final EvsHalService mEvsHal;
private final Object mLock = new Object();
/** Might be re-assigned if Vehicle HAL is reconnected. */
private volatile HalClient mHalClient;
/** Stores handler for each HAL property. Property events are sent to handler. */
@GuardedBy("mLock")
private final SparseArray<HalServiceBase> mPropertyHandlers = new SparseArray<>();
/** This is for iterating all HalServices with fixed order. */
@GuardedBy("mLock")
private final ArrayList<HalServiceBase> mAllServices = new ArrayList<>();
@GuardedBy("mLock")
private final HashMap<Integer, SubscribeOptions> mSubscribedProperties = new HashMap<>();
@GuardedBy("mLock")
private final HashMap<Integer, VehiclePropConfig> mAllProperties = new HashMap<>();
@GuardedBy("mLock")
private final HashMap<Integer, VehiclePropertyEventInfo> mEventLog = new HashMap<>();
// Used by injectVHALEvent for testing purposes. Delimiter for an array of data
private static final String DATA_DELIMITER = ",";
/**
* Constructs a new {@link VehicleHal} object given the {@link Context} and {@link IVehicle}
* both passed as parameters.
*/
public VehicleHal(Context context, IVehicle vehicle) {
mPowerHal = new PowerHalService(this);
mPropertyHal = new PropertyHalService(this);
mInputHal = new InputHalService(this);
mVmsHal = new VmsHalService(context, this);
mUserHal = new UserHalService(this);
mDiagnosticHal = new DiagnosticHalService(this);
mClusterHalService = new ClusterHalService(this);
mEvsHal = new EvsHalService(this);
mAllServices.addAll(Arrays.asList(mPowerHal,
mInputHal,
mDiagnosticHal,
mVmsHal,
mUserHal,
mClusterHalService,
mEvsHal,
mPropertyHal)); // mPropertyHal should be the last.
mHalClient = new HalClient(vehicle, mHandlerThread.getLooper(),
/* callback= */ this);
}
/**
* Constructs a new {@link VehicleHal} object given the services and {@link HalClient} factory
* function passed as parameters. This method must be used by tests only.
*/
@VisibleForTesting
VehicleHal(PowerHalService powerHal,
PropertyHalService propertyHal,
InputHalService inputHal,
VmsHalService vmsHal,
UserHalService userHal,
DiagnosticHalService diagnosticHal,
ClusterHalService clusterHalService,
HalClient halClient) {
mPowerHal = powerHal;
mPropertyHal = propertyHal;
mInputHal = inputHal;
mVmsHal = vmsHal;
mUserHal = userHal;
mDiagnosticHal = diagnosticHal;
mClusterHalService = clusterHalService;
mEvsHal = new EvsHalService(this);
mAllServices.addAll(Arrays.asList(mPowerHal,
mInputHal,
mDiagnosticHal,
mVmsHal,
mUserHal,
mEvsHal,
mPropertyHal));
mHalClient = halClient;
}
/** Called when connection to Vehicle HAL was restored. */
public void vehicleHalReconnected(IVehicle vehicle) {
synchronized (mLock) {
mHalClient = new HalClient(vehicle, mHandlerThread.getLooper(),
this /*IVehicleCallback*/);
SubscribeOptions[] options = mSubscribedProperties.values()
.toArray(new SubscribeOptions[0]);
try {
mHalClient.subscribe(options);
} catch (RemoteException e) {
throw new RuntimeException("Failed to subscribe: " + Arrays.asList(options), e);
}
}
}
private void fetchAllPropConfigs() {
synchronized (mLock) {
if (!mAllProperties.isEmpty()) { // already set
Slog.i(CarLog.TAG_HAL, "fetchAllPropConfigs already fetched");
return;
}
}
ArrayList<VehiclePropConfig> configs;
try {
configs = mHalClient.getAllPropConfigs();
if (configs == null || configs.size() == 0) {
Slog.e(CarLog.TAG_HAL, "getAllPropConfigs returned empty configs");
return;
}
} catch (RemoteException e) {
throw new RuntimeException("Unable to retrieve vehicle property configuration", e);
}
synchronized (mLock) {
// Create map of all properties
for (VehiclePropConfig p : configs) {
if (DBG) {
Slog.i(CarLog.TAG_HAL, "Add config for prop:" + Integer.toHexString(p.prop)
+ " config:" + p);
}
mAllProperties.put(p.prop, p);
}
}
}
/**
* Inits the vhal configurations.
*
* <p><Note that {@link #getIfAvailableOrFailForEarlyStage(int, int)}
* can be called before {@code init()}.
*/
public void init() {
fetchAllPropConfigs();
// 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) {
for (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();
}
}
/**
* Releases all connected services (power management service, input service, etc).
*/
public void release() {
// release in reverse order from init
for (int i = mAllServices.size() - 1; i >= 0; i--) {
mAllServices.get(i).release();
}
synchronized (mLock) {
for (int p : mSubscribedProperties.keySet()) {
try {
mHalClient.unsubscribe(p);
} catch (RemoteException e) {
// Ignore exceptions on shutdown path.
Slog.w(CarLog.TAG_HAL, "Failed to unsubscribe", e);
}
}
mSubscribedProperties.clear();
mAllProperties.clear();
}
// keep the looper thread as should be kept for the whole life cycle.
}
public DiagnosticHalService getDiagnosticHal() {
return mDiagnosticHal;
}
public PowerHalService getPowerHal() {
return mPowerHal;
}
public PropertyHalService getPropertyHal() {
return mPropertyHal;
}
public InputHalService getInputHal() {
return mInputHal;
}
public UserHalService getUserHal() {
return mUserHal;
}
public VmsHalService getVmsHal() {
return mVmsHal;
}
public ClusterHalService getClusterHal() {
return mClusterHalService;
}
public EvsHalService getEvsHal() {
return mEvsHal;
}
private void assertServiceOwnerLocked(HalServiceBase service, int property) {
if (service != mPropertyHandlers.get(property)) {
throw new IllegalArgumentException("Property 0x" + toHexString(property)
+ " is not owned by service: " + service);
}
}
/**
* Subscribes given properties with sampling rate defaults to 0 and no special flags provided.
*
* @see #subscribeProperty(HalServiceBase, int, float, int)
*/
public void subscribeProperty(HalServiceBase service, int property)
throws IllegalArgumentException {
subscribeProperty(service, property, /* samplingRateHz= */ 0f,
SubscribeFlags.EVENTS_FROM_CAR);
}
/**
* Subscribes given properties with default subscribe flag.
*
* @see #subscribeProperty(HalServiceBase, int, float, int)
*/
public void subscribeProperty(HalServiceBase service, int property, float sampleRateHz)
throws IllegalArgumentException {
subscribeProperty(service, property, sampleRateHz, SubscribeFlags.EVENTS_FROM_CAR);
}
/**
* Subscribe given property. Only Hal service owning the property can subscribe it.
*
* @param service HalService that owns this property
* @param property property id (VehicleProperty)
* @param samplingRateHz sampling rate in Hz for continuous properties
* @param flags flags from {@link android.hardware.automotive.vehicle.V2_0.SubscribeFlags}
* @throws IllegalArgumentException thrown if property is not supported by VHAL
*/
public void subscribeProperty(HalServiceBase service, int property,
float samplingRateHz, int flags) throws IllegalArgumentException {
if (DBG) {
Slog.i(CarLog.TAG_HAL, "subscribeProperty, service:" + service
+ ", " + toCarPropertyLog(property));
}
VehiclePropConfig config;
synchronized (mLock) {
config = mAllProperties.get(property);
}
if (config == null) {
throw new IllegalArgumentException("subscribe error: config is null for property 0x"
+ toHexString(property));
} else if (isPropertySubscribable(config)) {
SubscribeOptions opts = new SubscribeOptions();
opts.propId = property;
opts.sampleRate = samplingRateHz;
opts.flags = flags;
synchronized (mLock) {
assertServiceOwnerLocked(service, property);
mSubscribedProperties.put(property, opts);
}
try {
mHalClient.subscribe(opts);
} catch (RemoteException e) {
Slog.e(CarLog.TAG_HAL, "Failed to subscribe to " + toCarPropertyLog(property),
e);
}
} else {
Slog.e(CarLog.TAG_HAL, "Cannot subscribe to " + toCarPropertyLog(property));
}
}
/**
* Unsubscribes from receiving notifications for the property and HAL services passed
* as parameters.
*/
public void unsubscribeProperty(HalServiceBase service, int property) {
if (DBG) {
Slog.i(CarLog.TAG_HAL, "unsubscribeProperty, service:" + service
+ ", " + toCarPropertyLog(property));
}
VehiclePropConfig config;
synchronized (mLock) {
config = mAllProperties.get(property);
}
if (config == null) {
Slog.e(CarLog.TAG_HAL, "unsubscribeProperty " + toCarPropertyLog(property)
+ " does not exist");
} else if (isPropertySubscribable(config)) {
synchronized (mLock) {
assertServiceOwnerLocked(service, property);
mSubscribedProperties.remove(property);
}
try {
mHalClient.unsubscribe(property);
} catch (RemoteException e) {
Slog.e(CarLog.TAG_SERVICE, "Failed to unsubscribe: "
+ toCarPropertyLog(property), e);
}
} else {
Slog.e(CarLog.TAG_HAL, "Cannot unsubscribe " + toCarPropertyLog(property));
}
}
/**
* Indicates if the property passed as parameter is supported.
*/
public boolean isPropertySupported(int propertyId) {
synchronized (mLock) {
return mAllProperties.containsKey(propertyId);
}
}
/**
* Gets given property with retries.
*
* <p>If getting the property fails after all retries, it will throw
* {@code IllegalStateException}. If the property does not exist, it will simply return
* {@code null}.
*/
public @Nullable VehiclePropValue getIfAvailableOrFail(int propertyId, int numberOfRetries) {
if (!isPropertySupported(propertyId)) {
return null;
}
VehiclePropValue value;
for (int i = 0; i < numberOfRetries; i++) {
try {
return get(propertyId);
} catch (ServiceSpecificException e) {
Slog.e(CarLog.TAG_HAL, "Cannot get " + toCarPropertyLog(propertyId), e);
}
}
throw new IllegalStateException("Cannot get property: 0x" + toHexString(propertyId)
+ " after " + numberOfRetries + " retries");
}
/**
* This works similar to {@link #getIfAvailableOrFail(int, int)} except that this can be called
* before {@code init()} is called.
*
* <p>This call will check if requested vhal property is supported by querying directly to vhal
* and can have worse performance. Use this only for accessing vhal properties before
* {@code ICarImpl.init()} phase.
*/
public @Nullable VehiclePropValue getIfAvailableOrFailForEarlyStage(int propertyId,
int numberOfRetries) {
fetchAllPropConfigs();
return getIfAvailableOrFail(propertyId, numberOfRetries);
}
/**
* Returns the property's {@link VehiclePropValue} for the property id passed as parameter and
* not specified area.
*/
public VehiclePropValue get(int propertyId) {
return get(propertyId, NO_AREA);
}
/**
* Returns the property's {@link VehiclePropValue} for the property id and area id passed as
* parameters.
*/
public VehiclePropValue get(int propertyId, int areaId) {
if (DBG) {
Slog.i(CarLog.TAG_HAL, "get, " + toCarPropertyLog(propertyId)
+ toCarAreaLog(areaId));
}
return mHalClient.getValue(createPropValue(propertyId, areaId));
}
/**
* Returns the property object value for the class and property id passed as parameter and
* no area specified.
*/
public <T> T get(Class clazz, int propertyId) {
return get(clazz, createPropValue(propertyId, NO_AREA));
}
/**
* Returns the property object value for the class, property id, and area id passed as
* parameter.
*/
public <T> T get(Class clazz, int propertyId, int areaId) {
return get(clazz, createPropValue(propertyId, areaId));
}
/**
* Returns the property object value for the class and requested property value passed as
* parameter.
*/
@SuppressWarnings("unchecked")
public <T> T get(Class clazz, VehiclePropValue requestedPropValue) {
VehiclePropValue propValue;
propValue = mHalClient.getValue(requestedPropValue);
if (clazz == Integer.class || clazz == int.class) {
return (T) propValue.value.int32Values.get(0);
} else if (clazz == Boolean.class || clazz == boolean.class) {
return (T) Boolean.valueOf(propValue.value.int32Values.get(0) == 1);
} else if (clazz == Float.class || clazz == float.class) {
return (T) propValue.value.floatValues.get(0);
} else if (clazz == Integer[].class) {
Integer[] intArray = new Integer[propValue.value.int32Values.size()];
return (T) propValue.value.int32Values.toArray(intArray);
} else if (clazz == Float[].class) {
Float[] floatArray = new Float[propValue.value.floatValues.size()];
return (T) propValue.value.floatValues.toArray(floatArray);
} else if (clazz == int[].class) {
return (T) toIntArray(propValue.value.int32Values);
} else if (clazz == float[].class) {
return (T) toFloatArray(propValue.value.floatValues);
} else if (clazz == byte[].class) {
return (T) toByteArray(propValue.value.bytes);
} else if (clazz == String.class) {
return (T) propValue.value.stringValue;
} else {
throw new IllegalArgumentException("Unexpected type: " + clazz);
}
}
/**
* Returns the vehicle's {@link VehiclePropValue} for the requested property value passed
* as parameter.
*/
public VehiclePropValue get(VehiclePropValue requestedPropValue) {
return mHalClient.getValue(requestedPropValue);
}
/**
* Returns the sample rate for a subscribed property. Returns {@link VehicleHal#NO_SAMPLE_RATE}
* if the property id passed as parameter is not linked to any subscribed property.
*/
public float getSampleRate(int propId) {
SubscribeOptions opts = mSubscribedProperties.get(propId);
if (opts == null) {
// No sample rate for this property
return NO_SAMPLE_RATE;
} else {
return opts.sampleRate;
}
}
protected void set(VehiclePropValue propValue) {
mHalClient.setValue(propValue);
}
@CheckResult
VehiclePropValueSetter set(int propId) {
return new VehiclePropValueSetter(mHalClient, propId, NO_AREA);
}
@CheckResult
VehiclePropValueSetter set(int propId, int areaId) {
return new VehiclePropValueSetter(mHalClient, propId, areaId);
}
static boolean isPropertySubscribable(VehiclePropConfig config) {
if ((config.access & VehiclePropertyAccess.READ) == 0
|| (config.changeMode == VehiclePropertyChangeMode.STATIC)) {
return false;
}
return true;
}
private final ArraySet<HalServiceBase> mServicesToDispatch = new ArraySet<>();
@Override
public void onPropertyEvent(ArrayList<VehiclePropValue> propValues) {
synchronized (mLock) {
for (VehiclePropValue v : propValues) {
HalServiceBase service = mPropertyHandlers.get(v.prop);
if (service == null) {
Slog.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();
}
@Override
public void onPropertySet(VehiclePropValue value) {
// No need to handle on-property-set events in HAL service yet.
}
@Override
public void onPropertySetError(@CarPropertyManager.CarSetPropertyErrorCode int errorCode,
int propId, int areaId) {
Slog.e(CarLog.TAG_HAL, String.format("onPropertySetError, errorCode: %d, prop: 0x%x, "
+ "area: 0x%x", errorCode, propId, areaId));
if (propId != VehicleProperty.INVALID) {
HalServiceBase service = mPropertyHandlers.get(propId);
if (service != null) {
service.onPropertySetError(propId, areaId, errorCode);
}
}
}
/**
* Dumps HAL service info using the print writer passed as parameter.
*/
@ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
public void dump(PrintWriter writer) {
writer.println("**dump HAL services**");
for (HalServiceBase service: mAllServices) {
service.dump(writer);
}
// Dump all VHAL property configure.
dumpPropertyConfigs(writer, -1);
writer.printf("**All Events, now ns:%d**\n",
SystemClock.elapsedRealtimeNanos());
for (VehiclePropertyEventInfo info : mEventLog.values()) {
writer.printf("event count:%d, lastEvent: ", info.mEventCount);
dumpVehiclePropValue(writer, info.mLastEvent);
}
writer.println("**Property handlers**");
for (int i = 0; i < mPropertyHandlers.size(); i++) {
int propId = mPropertyHandlers.keyAt(i);
HalServiceBase service = mPropertyHandlers.valueAt(i);
writer.printf("Property Id: %d // 0x%x name: %s, service: %s\n", propId, propId,
VehiclePropertyIds.toString(propId), service);
}
}
/**
* Dumps the list of HALs.
*/
public void dumpListHals(PrintWriter writer) {
for (HalServiceBase service: mAllServices) {
writer.println(service.getClass().getName());
}
}
/**
* Dumps the given HALs.
*/
public void dumpSpecificHals(PrintWriter writer, String... halNames) {
Map<String, HalServiceBase> byName = mAllServices.stream()
.collect(Collectors.toMap(s -> s.getClass().getSimpleName(), s -> s));
for (String halName : halNames) {
HalServiceBase service = byName.get(halName);
if (service == null) {
writer.printf("No HAL named %s. Valid options are: %s\n", halName, byName.keySet());
continue;
}
service.dump(writer);
}
}
/**
* Dumps vehicle property values.
* @param writer
* @param propId property id, dump all properties' value if it is empty string.
* @param areaId areaId of the property, dump the property for all areaIds in the config
* if it is empty string.
*/
public void dumpPropertyValueByCommend(PrintWriter writer, int propId, int areaId) {
if (propId == -1) {
writer.println("**All property values**");
for (VehiclePropConfig config : mAllProperties.values()) {
dumpPropertyValueByConfig(writer, config);
}
} else if (areaId == -1) {
VehiclePropConfig config = mAllProperties.get(propId);
if (config == null) {
writer.print("Property ");
dumpPropHelper(writer, propId);
writer.print(" not supported by HAL\n");
return;
}
dumpPropertyValueByConfig(writer, config);
} else {
try {
VehiclePropValue value = get(propId, areaId);
dumpVehiclePropValue(writer, value);
} catch (RuntimeException e) {
writer.printf("Can not get property value for property: %d // 0x%x "
+ "in areaId: %d // 0x%x.\n", propId, propId, areaId, areaId);
}
}
}
private static void dumpPropHelper(PrintWriter pw, int propId) {
pw.printf("Id: %d // 0x%x, name: %s ", propId, propId, VehiclePropertyIds.toString(propId));
}
private void dumpPropertyValueByConfig(PrintWriter writer, VehiclePropConfig config) {
if (config.areaConfigs.isEmpty()) {
try {
VehiclePropValue value = get(config.prop);
dumpVehiclePropValue(writer, value);
} catch (RuntimeException e) {
writer.printf("Can not get property value for property: %d // 0x%x,"
+ " areaId: 0 \n", config.prop, config.prop);
}
} else {
for (VehicleAreaConfig areaConfig : config.areaConfigs) {
int area = areaConfig.areaId;
try {
VehiclePropValue value = get(config.prop, area);
dumpVehiclePropValue(writer, value);
} catch (RuntimeException e) {
writer.printf("Can not get property value for property: %d // 0x%x "
+ "in areaId: %d // 0x%x\n", config.prop, config.prop , area, area);
}
}
}
}
/**
* Dump VHAL property configs.
*
* @param writer
* @param propId Property ID. If propid is empty string, dump all properties.
*/
public void dumpPropertyConfigs(PrintWriter writer, int propId) {
List<VehiclePropConfig> configList;
synchronized (mLock) {
configList = new ArrayList<>(mAllProperties.values());
}
if (propId == -1) {
writer.println("**All properties**");
for (VehiclePropConfig config : configList) {
dumpPropertyConfigsHelp(writer, config);
}
return;
}
for (VehiclePropConfig config : configList) {
if (config.prop == propId) {
dumpPropertyConfigsHelp(writer, config);
return;
}
}
}
/** Dumps VehiclePropertyConfigs */
private static void dumpPropertyConfigsHelp(PrintWriter writer, VehiclePropConfig config) {
writer.printf("Property:0x%x, Property name:%s, access:0x%x, changeMode:0x%x, "
+ "config:%s, fs min:%f, fs max:%f\n",
config.prop, VehiclePropertyIds.toString(config.prop), config.access,
config.changeMode, Arrays.toString(config.configArray.toArray()),
config.minSampleRate, config.maxSampleRate);
for (VehicleAreaConfig area : config.areaConfigs) {
writer.printf("\tareaId:0x%x, f min:%f, f max:%f, i min:%d, i max:%d,"
+ " i64 min:%d, i64 max:%d\n",
area.areaId, area.minFloatValue, area.maxFloatValue, area.minInt32Value,
area.maxInt32Value, area.minInt64Value, area.maxInt64Value);
}
}
/**
* Inject a VHAL event
*
* @param property the Vehicle property Id as defined in the HAL
* @param zone Zone that this event services
* @param value Data value of the event
* @param delayTime Add a certain duration to event timestamp
*/
public void injectVhalEvent(int property, int zone, String value, int delayTime)
throws NumberFormatException {
VehiclePropValue v = createPropValueForInjecting(property, zone,
Arrays.asList(value.split(DATA_DELIMITER)));
if (v == null) {
return;
}
// update timestamp
v.timestamp = SystemClock.elapsedRealtimeNanos() + TimeUnit.SECONDS.toNanos(delayTime);
onPropertyEvent(Lists.newArrayList(v));
}
/**
* Injects continuous VHAL events.
*
* @param property the Vehicle property Id as defined in the HAL
* @param zone Zone that this event services
* @param value Data value of the event
* @param sampleRate Sample Rate for events in Hz
* @param timeDurationInSec The duration for injecting events in seconds
*/
public void injectContinuousVhalEvent(int property, int zone, String value,
float sampleRate, long timeDurationInSec) {
VehiclePropValue v = createPropValueForInjecting(property, zone,
new ArrayList<>(Arrays.asList(value.split(DATA_DELIMITER))));
if (v == null) {
return;
}
// rate in Hz
if (sampleRate <= 0) {
Slog.e(CarLog.TAG_HAL, "Inject events at an invalid sample rate: " + sampleRate);
return;
}
long period = (long) (1000 / sampleRate);
long stopTime = timeDurationInSec * 1000 + SystemClock.elapsedRealtime();
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
if (stopTime < SystemClock.elapsedRealtime()) {
timer.cancel();
timer.purge();
} else {
// Avoid the fake events be covered by real Event
v.timestamp = SystemClock.elapsedRealtimeNanos()
+ TimeUnit.SECONDS.toNanos(timeDurationInSec);
onPropertyEvent(Lists.newArrayList(v));
}
}
}, /* delay= */0, period);
}
// Returns null if the property type is unsupported.
@Nullable
private static VehiclePropValue createPropValueForInjecting(int propId, int zoneId,
List<String> dataList) {
VehiclePropValue v = createPropValue(propId, zoneId);
int propertyType = propId & VehiclePropertyType.MASK;
// Values can be comma separated list
switch (propertyType) {
case VehiclePropertyType.BOOLEAN:
boolean boolValue = Boolean.parseBoolean(dataList.get(0));
v.value.int32Values.add(boolValue ? 1 : 0);
break;
case VehiclePropertyType.INT32:
case VehiclePropertyType.INT32_VEC:
for (String s : dataList) {
v.value.int32Values.add(Integer.decode(s));
}
break;
case VehiclePropertyType.FLOAT:
case VehiclePropertyType.FLOAT_VEC:
for (String s : dataList) {
v.value.floatValues.add(Float.parseFloat(s));
}
break;
default:
Slog.e(CarLog.TAG_HAL, "Property type unsupported:" + propertyType);
return null;
}
return v;
}
private static class VehiclePropertyEventInfo {
private int mEventCount;
private VehiclePropValue mLastEvent;
private VehiclePropertyEventInfo(VehiclePropValue event) {
mEventCount = 1;
mLastEvent = event;
}
private void addNewEvent(VehiclePropValue event) {
mEventCount++;
mLastEvent = event;
}
}
final class VehiclePropValueSetter {
final WeakReference<HalClient> mClient;
final VehiclePropValue mPropValue;
private VehiclePropValueSetter(HalClient client, int propId, int areaId) {
mClient = new WeakReference<>(client);
mPropValue = new VehiclePropValue();
mPropValue.prop = propId;
mPropValue.areaId = areaId;
}
void to(boolean value) {
to(value ? 1 : 0);
}
void to(int value) {
mPropValue.value.int32Values.add(value);
submit();
}
void to(int[] values) {
for (int value : values) {
mPropValue.value.int32Values.add(value);
}
submit();
}
void to(Collection<Integer> values) {
mPropValue.value.int32Values.addAll(values);
submit();
}
void submit() {
HalClient client = mClient.get();
if (client != null) {
if (DBG) {
Slog.i(CarLog.TAG_HAL, "set, " + toCarPropertyLog(mPropValue.prop)
+ toCarAreaLog(mPropValue.areaId));
}
client.setValue(mPropValue);
}
}
}
private static void dumpVehiclePropValue(PrintWriter writer, VehiclePropValue value) {
String bytesString = "";
if (value.value.bytes.size() > MAX_BYTE_SIZE) {
Object[] bytes = Arrays.copyOf(value.value.bytes.toArray(), MAX_BYTE_SIZE);
bytesString = Arrays.toString(bytes);
} else {
bytesString = Arrays.toString(value.value.bytes.toArray());
}
writer.printf("Property:0x%x, status: %d, timestamp: %d, zone: 0x%x, "
+ "floatValues: %s, int32Values: %s, int64Values: %s, bytes: %s, string: %s\n",
value.prop, value.status, value.timestamp, value.areaId,
Arrays.toString(value.value.floatValues.toArray()),
Arrays.toString(value.value.int32Values.toArray()),
Arrays.toString(value.value.int64Values.toArray()),
bytesString, value.value.stringValue);
}
private static VehiclePropValue createPropValue(int propId, int areaId) {
VehiclePropValue propValue = new VehiclePropValue();
propValue.prop = propId;
propValue.areaId = areaId;
return propValue;
}
private static String toCarPropertyLog(int propId) {
return String.format("property Id: %d // 0x%x, property name: %s ", propId, propId,
VehiclePropertyIds.toString(propId));
}
private static String toCarAreaLog(int areaId) {
return String.format("areaId: %d // 0x%x", areaId, areaId);
}
}