blob: 6dc92db500fa151c74842f018eb53d58b1766108 [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.vehiclenetwork;
import static com.android.car.vehiclenetwork.VehiclePropValueUtil.getVectorLength;
import static com.android.car.vehiclenetwork.VehiclePropValueUtil.isCustomProperty;
import static com.android.car.vehiclenetwork.VehiclePropValueUtil.toFloatArray;
import static com.android.car.vehiclenetwork.VehiclePropValueUtil.toIntArray;
import android.annotation.IntDef;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.util.Log;
import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleValueType;
import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfigs;
import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValues;
import com.android.internal.annotations.GuardedBy;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
/**
* System API to access Vehicle network. This is only for system services and applications should
* not use this. All APIs will fail with security error if normal app tries this.
*/
public class VehicleNetwork {
/**
* Listener for VNS events.
*/
public interface VehicleNetworkListener {
/**
* Notify HAL events. This requires subscribing the property
*/
void onVehicleNetworkEvents(VehiclePropValues values);
void onHalError(int errorCode, int property, int operation);
void onHalRestart(boolean inMocking);
void onPropertySet(VehiclePropValue value);
}
public interface VehicleNetworkHalMock {
VehiclePropConfigs onListProperties();
void onPropertySet(VehiclePropValue value);
VehiclePropValue onPropertyGet(VehiclePropValue value);
void onPropertySubscribe(int property, float sampleRate, int zones);
void onPropertyUnsubscribe(int property);
}
/**
* Flags to be used in #subscribe(int, float, int, int).
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
SubscribeFlags.HAL_EVENT,
SubscribeFlags.SET_CALL,
SubscribeFlags.DEFAULT,
})
public @interface SubscribeFlags {
int HAL_EVENT = 0x1;
int SET_CALL = 0x2;
int DEFAULT = HAL_EVENT;
}
private static final String TAG = VehicleNetwork.class.getSimpleName();
private final IVehicleNetwork mService;
private final VehicleNetworkListener mListener;
private final IVehicleNetworkListenerImpl mVehicleNetworkListener;
private final EventHandler mEventHandler;
@GuardedBy("this")
private VehicleNetworkHalMock mHalMock;
private IVehicleNetworkHalMock mHalMockImpl;
private static final int VNS_CONNECT_MAX_RETRY = 10;
private static final long VNS_RETRY_WAIT_TIME_MS = 1000;
private static final int NO_ZONE = -1;
/**
* Factory method to create VehicleNetwork
*
* @param listener listener for listening events
* @param looper Looper to dispatch listener events
*/
public static VehicleNetwork createVehicleNetwork(VehicleNetworkListener listener,
Looper looper) {
int retryCount = 0;
IVehicleNetwork service = null;
while (true) {
service = IVehicleNetwork.Stub.asInterface(ServiceManager.getService(
IVehicleNetwork.class.getCanonicalName()));
if (service != null) {
break;
}
retryCount++;
if (retryCount > VNS_CONNECT_MAX_RETRY) {
break;
}
try {
Thread.sleep(VNS_RETRY_WAIT_TIME_MS);
} catch (InterruptedException e) {
//ignore
}
}
if (service == null) {
throw new RuntimeException("Vehicle network service not available:" +
IVehicleNetwork.class.getCanonicalName());
}
return new VehicleNetwork(service, listener, looper);
}
private VehicleNetwork(IVehicleNetwork service, VehicleNetworkListener listener,
Looper looper) {
mService = service;
mListener = listener;
mEventHandler = new EventHandler(looper);
mVehicleNetworkListener = new IVehicleNetworkListenerImpl(this);
}
/**
* List all properties from vehicle HAL
*
* @return all properties
*/
public VehiclePropConfigs listProperties() {
return listProperties(0 /* all */);
}
/**
* Return configuration information of single property
*
* @param property vehicle property number defined in {@link VehicleNetworkConsts}. 0 has has
* special meaning of list all properties.
* @return null if given property does not exist.
*/
public VehiclePropConfigs listProperties(int property) {
try {
VehiclePropConfigsParcelable parcelable = mService.listProperties(property);
if (parcelable != null) {
return parcelable.configs;
}
} catch (RemoteException e) {
handleRemoteException(e);
}
return null;
}
/**
* Set property which will lead into writing the value to vehicle HAL.
*
* @throws IllegalArgumentException If value set has wrong value like wrong valueType, wrong
* data, and etc.
*/
public void setProperty(VehiclePropValue value)
throws IllegalArgumentException, ServiceSpecificException {
VehiclePropValueParcelable parcelable = new VehiclePropValueParcelable(value);
try {
mService.setProperty(parcelable);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
/**
* Set integer type property
*
* @throws IllegalArgumentException For type mismatch (=the property is not int type)
*/
public void setIntProperty(int property, int value)
throws IllegalArgumentException, ServiceSpecificException {
VehiclePropValue v = VehiclePropValueUtil.createIntValue(property, value, 0);
setProperty(v);
}
/**
* Set int vector type property. Length of passed values should match with vector length.
*/
public void setIntVectorProperty(int property, int[] values)
throws IllegalArgumentException, ServiceSpecificException {
VehiclePropValue v = VehiclePropValueUtil.createIntVectorValue(property, values, 0);
setProperty(v);
}
/**
* Set long type property.
*/
public void setLongProperty(int property, long value)
throws IllegalArgumentException, ServiceSpecificException {
VehiclePropValue v = VehiclePropValueUtil.createLongValue(property, value, 0);
setProperty(v);
}
/**
* Set float type property.
*/
public void setFloatProperty(int property, float value)
throws IllegalArgumentException, ServiceSpecificException {
VehiclePropValue v = VehiclePropValueUtil.createFloatValue(property, value, 0);
setProperty(v);
}
/**
* Set float vector type property. Length of values should match with vector length.
*/
public void setFloatVectorProperty(int property, float[] values)
throws IllegalArgumentException, ServiceSpecificException {
VehiclePropValue v = VehiclePropValueUtil.createFloatVectorValue(property, values, 0);
setProperty(v);
}
/**
* Set zoned boolean type property
*
* @throws IllegalArgumentException For type mismatch (=the property is not boolean type)
*/
public void setBooleanProperty(int property, boolean value)
throws IllegalArgumentException, ServiceSpecificException {
VehiclePropValue v = VehiclePropValueUtil.createBooleanValue(property, value, 0);
setProperty(v);
}
public void setStringProperty(int property, String value)
throws IllegalArgumentException, ServiceSpecificException {
VehiclePropValue v = VehiclePropValueUtil.createStringValue(property, value, 0);
setProperty(v);
}
/**
* Set zoned boolean type property
*
* @throws IllegalArgumentException For type mismatch (=the property is not boolean type)
*/
public void setZonedBooleanProperty(int property, int zone, boolean value)
throws IllegalArgumentException, ServiceSpecificException {
VehiclePropValue v = VehiclePropValueUtil.createZonedBooleanValue(property, zone, value, 0);
setProperty(v);
}
/**
* Set zoned float type property
*
* @throws IllegalArgumentException For type mismatch (=the property is not float type)
*/
public void setZonedFloatProperty(int property, int zone, float value)
throws IllegalArgumentException, ServiceSpecificException {
VehiclePropValue v = VehiclePropValueUtil.createZonedFloatValue(property, zone, value, 0);
setProperty(v);
}
/**
* Set zoned integer type property
*
* @throws IllegalArgumentException For type mismatch (=the property is not int type)
*/
public void setZonedIntProperty(int property, int zone, int value)
throws IllegalArgumentException, ServiceSpecificException {
VehiclePropValue v = VehiclePropValueUtil.createZonedIntValue(property, zone, value, 0);
setProperty(v);
}
/**
* Set zoned int vector type property. Length of passed values should match with vector length.
*/
public void setZonedIntVectorProperty(int property, int zone, int[] values)
throws IllegalArgumentException, ServiceSpecificException {
VehiclePropValue v = VehiclePropValueUtil
.createZonedIntVectorValue(property, zone, values, 0);
setProperty(v);
}
/**
* Set zoned float vector type property. Length of passed values should match with vector
* length.
*/
public void setZonedFloatVectorProperty(int property, int zone, float[] values)
throws IllegalArgumentException, ServiceSpecificException {
VehiclePropValue v = VehiclePropValueUtil
.createZonedFloatVectorValue(property, zone, values, 0);
setProperty(v);
}
/**
* Get property. This can be used for a property which does not require any other data.
*/
public VehiclePropValue getProperty(int property) throws IllegalArgumentException,
ServiceSpecificException {
int valueType = VehicleNetworkConsts.getVehicleValueType(property);
if (valueType == 0) {
throw new IllegalArgumentException("Data type is unknown for property: " + property);
}
VehiclePropValue value = VehiclePropValueUtil.createBuilder(property, valueType, 0).build();
return getProperty(value);
}
/**
* Generic get method for any type of property. Some property may require setting data portion
* as get may return different result depending on the data set.
*/
public VehiclePropValue getProperty(VehiclePropValue value) throws IllegalArgumentException,
ServiceSpecificException {
VehiclePropValueParcelable parcelable = new VehiclePropValueParcelable(value);
try {
VehiclePropValueParcelable resParcelable = mService.getProperty(parcelable);
if (resParcelable != null) {
return resParcelable.value;
}
} catch (RemoteException e) {
handleRemoteException(e);
}
return null;
}
/**
* Get int type property.
*/
public int getIntProperty(int property) throws IllegalArgumentException,
ServiceSpecificException {
VehiclePropValue v = getProperty(
property, NO_ZONE, VehicleValueType.VEHICLE_VALUE_TYPE_INT32);
if (v.getInt32ValuesCount() != 1) {
throw new IllegalStateException();
}
return v.getInt32Values(0);
}
/**
* Get zoned int type property.
*/
public int getZonedIntProperty(int property, int zone) throws IllegalArgumentException,
ServiceSpecificException {
VehiclePropValue v = getProperty(
property, zone, VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32);
if (v.getInt32ValuesCount() != 1) {
throw new IllegalStateException();
}
return v.getInt32Values(0);
}
/**
* Get int vector type property. Length of values should match vector length.
*/
public int[] getIntVectorProperty(int property) throws IllegalArgumentException,
ServiceSpecificException {
VehiclePropValue v = getProperty(
property, NO_ZONE, VehicleValueType.VEHICLE_VALUE_TYPE_INT32);
assertVectorLength(v.getInt32ValuesCount(), property, v.getValueType());
return toIntArray(v.getInt32ValuesList());
}
/**
* Get zoned int vector type property. Length of values should match vector length.
*/
public int[] getZonedIntVectorProperty(int property, int zone)
throws IllegalArgumentException, ServiceSpecificException {
VehiclePropValue v = getProperty(
property, zone, VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32);
assertVectorLength(v.getInt32ValuesCount(), property, v.getValueType());
return toIntArray(v.getInt32ValuesList());
}
/**
* Get float type property.
*/
public float getFloatProperty(int property) throws IllegalArgumentException,
ServiceSpecificException {
VehiclePropValue v = getProperty(
property, NO_ZONE, VehicleValueType.VEHICLE_VALUE_TYPE_FLOAT);
if (v.getFloatValuesCount() != 1) {
throw new IllegalStateException();
}
return v.getFloatValues(0);
}
/**
* Get float vector type property. Length of values should match vector's length.
*/
public float[] getFloatVectorProperty(int property) throws IllegalArgumentException,
ServiceSpecificException {
VehiclePropValue v = getProperty(
property, NO_ZONE, VehicleValueType.VEHICLE_VALUE_TYPE_FLOAT);
assertVectorLength(v.getFloatValuesCount(), property, v.getValueType());
return toFloatArray(v.getFloatValuesList());
}
/**
* Get zoned float vector type property. Length of values should match vector's length.
*/
public float[] getZonedFloatVectorProperty(int property, int zone)
throws IllegalArgumentException, ServiceSpecificException {
VehiclePropValue v = getProperty(property, zone,
VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_FLOAT);
assertVectorLength(v.getFloatValuesCount(), property, v.getValueType());
return toFloatArray(v.getFloatValuesList());
}
/**
* Get long type property.
*/
public long getLongProperty(int property) throws IllegalArgumentException,
ServiceSpecificException {
VehiclePropValue v = getProperty(
property, NO_ZONE, VehicleValueType.VEHICLE_VALUE_TYPE_INT64);
return v.getInt64Value();
}
/**
* Get string type property.
*/
public String getStringProperty(int property) throws IllegalArgumentException,
ServiceSpecificException {
VehiclePropValue v = getProperty(
property, NO_ZONE, VehicleValueType.VEHICLE_VALUE_TYPE_STRING);
return v.getStringValue();
}
/**
* Subscribe given property with given sample rate.
*/
public void subscribe(int property, float sampleRate) throws IllegalArgumentException {
subscribe(property, sampleRate, 0);
}
/**
* Subscribe given property with given sample rate and zones.
*/
public void subscribe(int property, float sampleRate, int zones)
throws IllegalArgumentException {
try {
mService.subscribe(mVehicleNetworkListener, property, sampleRate, zones,
SubscribeFlags.DEFAULT);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
/**
* Subscribe given property with given sample rate, zones and flags.
*/
@SuppressWarnings("ResourceType")
public void subscribe(int property, float sampleRate, int zones, @SubscribeFlags int flags)
throws IllegalArgumentException {
try {
mService.subscribe(mVehicleNetworkListener, property, sampleRate, zones, flags);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
/**
* Stop subscribing the property.
*/
public void unsubscribe(int property) {
try {
mService.unsubscribe(mVehicleNetworkListener, property);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
/**
* Inject given value to all clients subscribing the property. This is for testing.
*/
public synchronized void injectEvent(VehiclePropValue value) {
try {
mService.injectEvent(new VehiclePropValueParcelable(value));
} catch (RemoteException e) {
handleRemoteException(e);
}
}
/**
* Start mocking of vehicle HAL. For testing only.
*/
public synchronized void startMocking(VehicleNetworkHalMock mock) {
mHalMock = mock;
mHalMockImpl = new IVehicleNetworkHalMockImpl(this);
try {
mService.startMocking(mHalMockImpl);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
/**
* Stop mocking of vehicle HAL. For testing only.
*/
public synchronized void stopMocking() {
if (mHalMockImpl == null) {
return;
}
try {
mService.stopMocking(mHalMockImpl);
} catch (RemoteException e) {
handleRemoteException(e);
} finally {
mHalMock = null;
mHalMockImpl = null;
}
}
/**
* Start mocking of vehicle HAL. For testing only.
*/
public synchronized void startMocking(IVehicleNetworkHalMock mock) {
mHalMock = null;
mHalMockImpl = mock;
try {
mService.startMocking(mHalMockImpl);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
/**
* Stop mocking of vehicle HAL. For testing only.
*/
public synchronized void stopMocking(IVehicleNetworkHalMock mock) {
if (mock.asBinder() != mHalMockImpl.asBinder()) {
return;
}
try {
mService.stopMocking(mHalMockImpl);
} catch (RemoteException e) {
handleRemoteException(e);
} finally {
mHalMock = null;
mHalMockImpl = null;
}
}
public synchronized void injectHalError(int errorCode, int property, int operation) {
try {
mService.injectHalError(errorCode, property, operation);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
public synchronized void startErrorListening() {
try {
mService.startErrorListening(mVehicleNetworkListener);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
public synchronized void stopErrorListening() {
try {
mService.stopErrorListening(mVehicleNetworkListener);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
public synchronized void startHalRestartMonitoring() {
try {
mService.startHalRestartMonitoring(mVehicleNetworkListener);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
public synchronized void stopHalRestartMonitoring() {
try {
mService.stopHalRestartMonitoring(mVehicleNetworkListener);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
private synchronized VehicleNetworkHalMock getHalMock() {
return mHalMock;
}
private void handleRemoteException(RemoteException e) {
throw new RuntimeException("Vehicle network service not working ", e);
}
private void handleVehicleNetworkEvents(VehiclePropValues values) {
mListener.onVehicleNetworkEvents(values);
}
private void handleHalError(int errorCode, int property, int operation) {
mListener.onHalError(errorCode, property, operation);
}
private void handleHalRestart(boolean inMocking) {
mListener.onHalRestart(inMocking);
}
private void handleOnPropertySet(VehiclePropValue value) {
mListener.onPropertySet(value);
}
private class EventHandler extends Handler {
private static final int MSG_EVENTS = 0;
private static final int MSG_HAL_ERROR = 1;
private static final int MSG_HAL_RESTART = 2;
private static final int MSG_ON_PROPERTY_SET = 3;
private EventHandler(Looper looper) {
super(looper);
}
private void notifyEvents(VehiclePropValues values) {
Message msg = obtainMessage(MSG_EVENTS, values);
sendMessage(msg);
}
private void notifyHalError(int errorCode, int property, int operation) {
Message msg = obtainMessage(MSG_HAL_ERROR, errorCode, property, operation);
sendMessage(msg);
}
private void notifyHalRestart(boolean inMocking) {
Message msg = obtainMessage(MSG_HAL_RESTART, inMocking ? 1 : 0, 0);
sendMessage(msg);
}
private void notifyPropertySet(VehiclePropValue value) {
Message msg = obtainMessage(MSG_ON_PROPERTY_SET, value);
sendMessage(msg);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_EVENTS:
handleVehicleNetworkEvents((VehiclePropValues) msg.obj);
break;
case MSG_HAL_ERROR:
handleHalError(msg.arg1, msg.arg2, (Integer) msg.obj);
break;
case MSG_HAL_RESTART:
handleHalRestart(msg.arg1 == 1);
break;
case MSG_ON_PROPERTY_SET:
handleOnPropertySet((VehiclePropValue) msg.obj);
default:
Log.w(TAG, "Unknown message:" + msg.what, new RuntimeException());
break;
}
}
}
private static class IVehicleNetworkListenerImpl extends IVehicleNetworkListener.Stub {
private final WeakReference<VehicleNetwork> mVehicleNetwork;
private IVehicleNetworkListenerImpl(VehicleNetwork vehicleNetwork) {
mVehicleNetwork = new WeakReference<>(vehicleNetwork);
}
@Override
public void onVehicleNetworkEvents(VehiclePropValuesParcelable values) {
VehicleNetwork vehicleNetwork = mVehicleNetwork.get();
if (vehicleNetwork != null) {
vehicleNetwork.mEventHandler.notifyEvents(values.values);
}
}
@Override
public void onPropertySet(VehiclePropValueParcelable value) {
VehicleNetwork vehicleNetwork = mVehicleNetwork.get();
if (vehicleNetwork != null) {
vehicleNetwork.mEventHandler.notifyPropertySet(value.value);
}
}
@Override
public void onHalError(int errorCode, int property, int operation) {
VehicleNetwork vehicleNetwork = mVehicleNetwork.get();
if (vehicleNetwork != null) {
vehicleNetwork.mEventHandler.notifyHalError(errorCode, property, operation);
}
}
@Override
public void onHalRestart(boolean inMocking) {
VehicleNetwork vehicleNetwork = mVehicleNetwork.get();
if (vehicleNetwork != null) {
vehicleNetwork.mEventHandler.notifyHalRestart(inMocking);
}
}
}
private static class IVehicleNetworkHalMockImpl extends IVehicleNetworkHalMock.Stub {
private final WeakReference<VehicleNetwork> mVehicleNetwork;
private IVehicleNetworkHalMockImpl(VehicleNetwork vehicleNewotk) {
mVehicleNetwork = new WeakReference<>(vehicleNewotk);
}
@Override
public VehiclePropConfigsParcelable onListProperties() {
VehicleNetwork vehicleNetwork = mVehicleNetwork.get();
if (vehicleNetwork == null) {
return null;
}
VehiclePropConfigs configs = vehicleNetwork.getHalMock().onListProperties();
return new VehiclePropConfigsParcelable(configs);
}
@Override
public void onPropertySet(VehiclePropValueParcelable value) {
VehicleNetwork vehicleNetwork = mVehicleNetwork.get();
if (vehicleNetwork == null) {
return;
}
vehicleNetwork.getHalMock().onPropertySet(value.value);
}
@Override
public VehiclePropValueParcelable onPropertyGet(VehiclePropValueParcelable value) {
VehicleNetwork vehicleNetwork = mVehicleNetwork.get();
if (vehicleNetwork == null) {
return null;
}
VehiclePropValue resValue = vehicleNetwork.getHalMock().onPropertyGet(value.value);
return new VehiclePropValueParcelable(resValue);
}
@Override
public void onPropertySubscribe(int property, float sampleRate, int zones) {
VehicleNetwork vehicleNetwork = mVehicleNetwork.get();
if (vehicleNetwork == null) {
return;
}
vehicleNetwork.getHalMock().onPropertySubscribe(property, sampleRate, zones);
}
@Override
public void onPropertyUnsubscribe(int property) {
VehicleNetwork vehicleNetwork = mVehicleNetwork.get();
if (vehicleNetwork == null) {
return;
}
vehicleNetwork.getHalMock().onPropertyUnsubscribe(property);
}
}
private VehiclePropValue getProperty(int property, int zone, int customPropetyDataType) {
boolean isCustom = isCustomProperty(property);
int valueType = isCustom
? customPropetyDataType
: VehicleNetworkConsts.getVehicleValueType(property);
VehiclePropValue.Builder valuePrototypeBuilder =
VehiclePropValueUtil.createBuilder(property, valueType, 0);
if (zone != NO_ZONE) {
valuePrototypeBuilder.setZone(zone);
}
VehiclePropValue v = getProperty(valuePrototypeBuilder.build());
if (v == null) {
// if property is invalid, IllegalArgumentException should have been thrown
// from getProperty.
throw new IllegalStateException();
}
if (!isCustom && v.getValueType() != valueType) {
throw new IllegalArgumentException(
"Unexpected type for property 0x" + Integer.toHexString(property) +
" got:0x" + Integer.toHexString(v.getValueType())
+ ", expecting:0x" + Integer.toHexString(valueType));
}
return v;
}
private void assertVectorLength(int actual, int property, int valueType) {
int expectedLen = getVectorLength(valueType);
if (expectedLen != actual) {
throw new IllegalStateException("Invalid array size for property: " + property
+ ". Expected: " + expectedLen
+ ", actual: " + actual);
}
}
}