aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkeunyoung <keunyoung@google.com>2015-08-12 10:46:27 -0700
committerRakesh Iyer <rni@google.com>2015-11-17 15:04:55 -0800
commitcc449f7941456a0133ff8a4b2e49737f0936c1d0 (patch)
tree14353f30f62be16c4a68cd0c75a182893fd0e659
parentca515079e9fc0c35b1498830f67378e9ccf949e5 (diff)
downloadCar-cc449f7941456a0133ff8a4b2e49737f0936c1d0.tar.gz
More details in Hal with sensor pipeline improvements for batching
- updated CarSensorEvent to use int array instead of byte as most use cases involve int value manipulations. - removed unused sensor types in CarSensorManager. They can brought back later as necessary. - CarSensorManager subscription works for several sensors. bug: 22701368 Change-Id: I2fb8901fe5266693aeae5f1b3f2b3adb75056a93 (cherry picked from commit d68bc574a27aee0fb6750ed301d247645d7bbb1e)
-rw-r--r--carsupport-lib/src/android/support/car/Car.java2
-rw-r--r--carsupport-lib/src/android/support/car/CarSensorEvent.java133
-rw-r--r--carsupport-lib/src/android/support/car/CarSensorManager.java59
-rw-r--r--carsupport-lib/src/android/support/car/ICarSensorEventListener.aidl2
-rw-r--r--service/Android.mk6
-rw-r--r--service/AndroidManifest.xml31
-rw-r--r--service/jni/Android.mk43
-rw-r--r--service/jni/HandlerThread.cpp72
-rw-r--r--service/jni/HandlerThread.h49
-rw-r--r--service/jni/IVehicleHalEventListener.h44
-rw-r--r--service/jni/JniInit.cpp42
-rw-r--r--service/jni/VehicleHal.cpp239
-rw-r--r--service/jni/VehicleHal.h111
-rw-r--r--service/jni/com_android_car_hal_Hal.cpp425
-rw-r--r--service/jni/vehicleHalConsts.h49
-rw-r--r--service/res/drawable-hdpi/car_ic_mode.pngbin0 -> 966 bytes
-rw-r--r--service/res/drawable-mdpi/car_ic_mode.pngbin0 -> 701 bytes
-rw-r--r--service/res/drawable-xhdpi/car_ic_mode.pngbin0 -> 1288 bytes
-rw-r--r--service/res/drawable-xxhdpi/car_ic_mode.pngbin0 -> 1370 bytes
-rw-r--r--service/res/values/strings.xml39
-rw-r--r--service/src/com/android/car/BootReceiver.java4
-rw-r--r--service/src/com/android/car/CarLog.java1
-rw-r--r--service/src/com/android/car/CarSensorEventFactory.java46
-rw-r--r--service/src/com/android/car/CarSensorService.java372
-rw-r--r--service/src/com/android/car/CarService.java6
-rw-r--r--service/src/com/android/car/CarServiceApplication.java35
-rw-r--r--service/src/com/android/car/DayNightModePolicy.java2
-rw-r--r--service/src/com/android/car/DrivingStatePolicy.java28
-rw-r--r--service/src/com/android/car/ICarImpl.java15
-rw-r--r--service/src/com/android/car/hal/Hal.java60
-rw-r--r--service/src/com/android/car/hal/HalProperty.java49
-rw-r--r--service/src/com/android/car/hal/HalPropertyConst.java83
-rw-r--r--service/src/com/android/car/hal/HalServiceBase.java52
-rw-r--r--service/src/com/android/car/hal/SensorHal.java75
-rw-r--r--service/src/com/android/car/hal/SensorHalService.java267
-rw-r--r--service/src/com/android/car/hal/SensorHalServiceBase.java (renamed from service/src/com/android/car/hal/SensorHalBase.java)46
-rw-r--r--service/src/com/android/car/hal/VehicleHal.java295
-rw-r--r--tests/api_test/Android.mk2
-rw-r--r--tests/car_activity_test_app/Android.mk2
-rw-r--r--tests/car_activity_test_app/AndroidManifest.xml2
-rw-r--r--tests/car_activity_test_app/res/layout/hello_caractivity.xml31
-rw-r--r--tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/HelloCarActivity.java42
42 files changed, 2471 insertions, 390 deletions
diff --git a/carsupport-lib/src/android/support/car/Car.java b/carsupport-lib/src/android/support/car/Car.java
index a0917f77b3..29f13faff7 100644
--- a/carsupport-lib/src/android/support/car/Car.java
+++ b/carsupport-lib/src/android/support/car/Car.java
@@ -141,7 +141,7 @@ public class Car {
if (mConnectionState == STATE_DISCONNECTED) {
return;
}
- mConnectionState = STATE_CONNECTING;
+ mConnectionState = STATE_DISCONNECTED;
}
mServiceConnectionListenerClient.onServiceDisconnected(name);
connect();
diff --git a/carsupport-lib/src/android/support/car/CarSensorEvent.java b/carsupport-lib/src/android/support/car/CarSensorEvent.java
index fa5a187afd..2b878f7bab 100644
--- a/carsupport-lib/src/android/support/car/CarSensorEvent.java
+++ b/carsupport-lib/src/android/support/car/CarSensorEvent.java
@@ -57,7 +57,7 @@ public class CarSensorEvent implements Parcelable {
public static final int INDEX_FUEL_LOW_WARNING = 0;
/**
- * GEAR_* represents meaning of byteValues[0] for {@link CarSensorManager#SENSOR_TYPE_GEAR}
+ * GEAR_* represents meaning of intValues[0] for {@link CarSensorManager#SENSOR_TYPE_GEAR}
* sensor type.
* GEAR_NEUTRAL means transmission gear is in neutral state, and the car may be moving.
*/
@@ -116,7 +116,7 @@ public class CarSensorEvent implements Parcelable {
DRIVE_STATUS_LIMIT_MESSAGE_LEN;
/**
* Index for {@link CarSensorManager#SENSOR_TYPE_LOCATION} in floatValues.
- * Each bit byteValues[0] represents whether the corresponding data is present.
+ * Each bit intValues[0] represents whether the corresponding data is present.
*/
public static final int INDEX_LOCATION_LATITUDE = 0;
public static final int INDEX_LOCATION_LONGITUDE = 1;
@@ -125,8 +125,8 @@ public class CarSensorEvent implements Parcelable {
public static final int INDEX_LOCATION_SPEED = 4;
public static final int INDEX_LOCATION_BEARING = 5;
public static final int INDEX_LOCATION_MAX = INDEX_LOCATION_BEARING;
- public static final int INDEX_LOCATION_LATITUDE_BYTES = 1;
- public static final int INDEX_LOCATION_LONGITUDE_BYTES = 5;
+ public static final int INDEX_LOCATION_LATITUDE_INTS = 1;
+ public static final int INDEX_LOCATION_LONGITUDE_INTS = 2;
/**
* Index for {@link CarSensorManager#SENSOR_TYPE_ENVIRONMENT} in floatValues.
@@ -140,17 +140,6 @@ public class CarSensorEvent implements Parcelable {
public static final int INDEX_ENVIRONMENT_PRESSURE = 1;
/**
- * Index for {@link CarSensorManager#SENSOR_TYPE_HVAC} in floatValues.
- * Target temperature set in Celsius degrees.
- */
- public static final int INDEX_HVAC_TARGET_TEMPERATURE = 0;
- /**
- * Index for {@link CarSensorManager#SENSOR_TYPE_HVAC} in floatValues.
- * The current temperature set in Celsius degrees.
- */
- public static final int INDEX_HVAC_CURRENT_TEMPERATURE = 1;
-
- /**
* Indices for {@link CarSensorManager#SENSOR_TYPE_COMPASS} in floatValues.
* Angles are in degrees. Pitch or/and roll can be NaN if it is not available.
*/
@@ -187,8 +176,8 @@ public class CarSensorEvent implements Parcelable {
*/
public static final int INDEX_GPS_SATELLITE_NUMBER_IN_USE = 0;
public static final int INDEX_GPS_SATELLITE_NUMBER_IN_VIEW = 1;
- public static final int INDEX_GPS_SATELLITE_ARRAY_BYTE_OFFSET = 2;
- public static final int INDEX_GPS_SATELLITE_ARRAY_BYTE_INTERVAL = 1;
+ public static final int INDEX_GPS_SATELLITE_ARRAY_INT_OFFSET = 2;
+ public static final int INDEX_GPS_SATELLITE_ARRAY_INT_INTERVAL = 1;
public static final int INDEX_GPS_SATELLITE_ARRAY_FLOAT_OFFSET = 0;
public static final int INDEX_GPS_SATELLITE_ARRAY_FLOAT_INTERVAL = 4;
public static final int INDEX_GPS_SATELLITE_PRN_OFFSET = 0;
@@ -210,20 +199,20 @@ public class CarSensorEvent implements Parcelable {
* array holding float type of sensor data. If the sensor has single value, only floatValues[0]
* should be used. */
public final float[] floatValues;
- /** array holding byte type of sensor data */
- public final byte[] byteValues;
+ /** array holding int type of sensor data */
+ public final int[] intValues;
public CarSensorEvent(
int versionCode,
int sensorType,
long timeStampNs,
float[] floatValues,
- byte[] byteValues) {
+ int[] intValues) {
this.mVersionCode = versionCode;
this.sensorType = sensorType;
this.timeStampNs = timeStampNs;
this.floatValues = floatValues;
- this.byteValues = byteValues;
+ this.intValues = intValues;
}
public CarSensorEvent(Parcel in) {
@@ -234,8 +223,8 @@ public class CarSensorEvent implements Parcelable {
floatValues = new float[len];
in.readFloatArray(floatValues);
len = in.readInt();
- byteValues = new byte[len];
- in.readByteArray(byteValues);
+ intValues = new int[len];
+ in.readIntArray(intValues);
// version 1 up to here
}
@@ -251,8 +240,8 @@ public class CarSensorEvent implements Parcelable {
dest.writeLong(timeStampNs);
dest.writeInt(floatValues.length);
dest.writeFloatArray(floatValues);
- dest.writeInt(byteValues.length);
- dest.writeByteArray(byteValues);
+ dest.writeInt(intValues.length);
+ dest.writeIntArray(intValues);
// version 1 up to here
}
@@ -274,21 +263,21 @@ public class CarSensorEvent implements Parcelable {
return mVersionCode;
}
- public CarSensorEvent(int sensorType, long timeStampNs, int floatValueSize, int byteValueSize) {
+ public CarSensorEvent(int sensorType, long timeStampNs, int floatValueSize, int intValueSize) {
mVersionCode = VERSION;
this.sensorType = sensorType;
this.timeStampNs = timeStampNs;
floatValues = new float[floatValueSize];
- byteValues = new byte[byteValueSize];
+ intValues = new int[intValueSize];
}
/** @hide */
- CarSensorEvent(int sensorType, long timeStampNs, float[] floatValues, byte[] byteValues) {
+ CarSensorEvent(int sensorType, long timeStampNs, float[] floatValues, int[] intValues) {
mVersionCode = VERSION;
this.sensorType = sensorType;
this.timeStampNs = timeStampNs;
this.floatValues = floatValues;
- this.byteValues = byteValues;
+ this.intValues = intValues;
}
private void checkType(int type) {
@@ -326,33 +315,6 @@ public class CarSensorEvent implements Parcelable {
return data;
}
- public static class HvacData {
- public long timeStampNs;
- /** If unsupported by the car, this value is NaN. */
- public float targetTemperature;
- /** If unsupported by the car, this value is NaN. */
- public float currentTemperature;
- }
-
- /**
- * Convenience method for obtaining a {@link HvacData} object from a CarSensorEvent
- * object with type {@link CarSensorManager#SENSOR_TYPE_HVAC}.
- *
- * @param data an optional output parameter which, if non-null, will be used by this method
- * instead of a newly created object.
- * @return a HvacData object corresponding to the data contained in the CarSensorEvent.
- */
- public HvacData getHvacData(HvacData data) {
- checkType(CarSensorManager.SENSOR_TYPE_HVAC);
- if (data == null) {
- data = new HvacData();
- }
- data.timeStampNs = timeStampNs;
- data.targetTemperature = floatValues[INDEX_ENVIRONMENT_TEMPERATURE];
- data.currentTemperature = floatValues[INDEX_ENVIRONMENT_PRESSURE];
- return data;
- }
-
public static class NightData {
public long timeStampNs;
public boolean isNightMode;
@@ -372,7 +334,7 @@ public class CarSensorEvent implements Parcelable {
data = new NightData();
}
data.timeStampNs = timeStampNs;
- data.isNightMode = byteValues[0] == 1;
+ data.isNightMode = intValues[0] == 1;
return data;
}
@@ -395,7 +357,7 @@ public class CarSensorEvent implements Parcelable {
data = new GearData();
}
data.timeStampNs = timeStampNs;
- data.gear = byteValues[0];
+ data.gear = intValues[0];
return data;
}
@@ -418,7 +380,7 @@ public class CarSensorEvent implements Parcelable {
data = new ParkingBrakeData();
}
data.timeStampNs = timeStampNs;
- data.isEngaged = byteValues[0] == 1;
+ data.isEngaged = intValues[0] == 1;
return data;
}
@@ -448,7 +410,7 @@ public class CarSensorEvent implements Parcelable {
data.timeStampNs = timeStampNs;
data.level = (int) floatValues[INDEX_FUEL_LEVEL_IN_PERCENTILE];
data.range = (int) floatValues[INDEX_FUEL_LEVEL_IN_DISTANCE];
- data.lowFuelWarning = byteValues[0] == 1;
+ data.lowFuelWarning = intValues[0] == 1;
return data;
}
@@ -563,15 +525,15 @@ public class CarSensorEvent implements Parcelable {
if (location == null) {
location = new Location("Car-GPS");
}
- // byteValues[0]: bit flags for the presence of other values following.
- int presense = byteValues[0];
+ // intValues[0]: bit flags for the presence of other values following.
+ int presense = intValues[0];
if ((presense & (0x1 << INDEX_LOCATION_LATITUDE)) != 0) {
- int latBytes = unpackBytes(byteValues, INDEX_LOCATION_LATITUDE_BYTES);
- location.setLatitude(latBytes * 1e-7);
+ int latE7 = intValues[INDEX_LOCATION_LATITUDE_INTS];
+ location.setLatitude(latE7 * 1e-7);
}
if ((presense & (0x1 << INDEX_LOCATION_LONGITUDE)) != 0) {
- int longBytes = unpackBytes(byteValues, INDEX_LOCATION_LONGITUDE_BYTES);
- location.setLongitude(longBytes * 1e-7);
+ int longE7 = intValues[INDEX_LOCATION_LONGITUDE_INTS];
+ location.setLongitude(longE7 * 1e-7);
}
if ((presense & (0x1 << INDEX_LOCATION_ACCURACY)) != 0) {
location.setAccuracy(floatValues[INDEX_LOCATION_ACCURACY]);
@@ -598,7 +560,7 @@ public class CarSensorEvent implements Parcelable {
public static class DrivingStatusData {
public long timeStampNs;
- public byte status;
+ public int status;
}
/**
@@ -614,7 +576,7 @@ public class CarSensorEvent implements Parcelable {
if (data == null) {
data = new DrivingStatusData();
}
- data.status = byteValues[0];
+ data.status = intValues[0];
return data;
}
@@ -733,14 +695,14 @@ public class CarSensorEvent implements Parcelable {
if (data == null) {
data = new GpsSatelliteData();
}
- final int byteOffset = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_BYTE_OFFSET;
- final int byteInterval = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_BYTE_INTERVAL;
+ final int intOffset = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_INT_OFFSET;
+ final int intInterval = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_INT_INTERVAL;
final int floatOffset = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_FLOAT_OFFSET;
final int floatInterval = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_FLOAT_INTERVAL;
final int numberOfSats = (floatValues.length - floatOffset) / floatInterval;
- data.numberInUse = byteValues[CarSensorEvent.INDEX_GPS_SATELLITE_NUMBER_IN_USE];
- data.numberInView = byteValues[CarSensorEvent.INDEX_GPS_SATELLITE_NUMBER_IN_VIEW];
+ data.numberInUse = intValues[CarSensorEvent.INDEX_GPS_SATELLITE_NUMBER_IN_USE];
+ data.numberInView = intValues[CarSensorEvent.INDEX_GPS_SATELLITE_NUMBER_IN_VIEW];
if (withPerSatellite && data.numberInView >= 0) {
data.usedInFix = new boolean[numberOfSats];
data.prn = new int[numberOfSats];
@@ -749,9 +711,9 @@ public class CarSensorEvent implements Parcelable {
data.elevation = new float[numberOfSats];
for (int i = 0; i < numberOfSats; ++i) {
- int iByte = byteOffset + byteInterval * i;
+ int iInt = intOffset + intInterval * i;
int iFloat = floatOffset + floatInterval * i;
- data.usedInFix[i] = byteValues[iByte] != 0;
+ data.usedInFix[i] = intValues[iInt] != 0;
data.prn[i] = Math.round(
floatValues[iFloat + CarSensorEvent.INDEX_GPS_SATELLITE_PRN_OFFSET]);
data.snr[i] =
@@ -766,23 +728,6 @@ public class CarSensorEvent implements Parcelable {
}
/** @hide */
- public static int unpackBytes(byte[] arr, int offset) {
- int b0 = arr[offset] & 0xff;
- int b1 = (arr[offset + 1] << 8) & 0xff00;
- int b2 = (arr[offset + 2] << 16) & 0xff0000;
- int b3 = (arr[offset + 3] << 24) & 0xff000000;
- return b0 | b1 | b2 | b3;
- }
-
- /** @hide */
- public static void packBytes(byte[] arr, int offset, int value) {
- arr[offset] = (byte) (value & 0xff);
- arr[offset + 1] = (byte) ((value >> 8) & 0xff);
- arr[offset + 2] = (byte) ((value >> 16) & 0xff);
- arr[offset + 3] = (byte) ((value >> 24) & 0xff);
- }
-
- /** @hide */
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -794,9 +739,9 @@ public class CarSensorEvent implements Parcelable {
sb.append(" " + v);
}
}
- if (byteValues != null && byteValues.length > 0) {
- sb.append(" byte values:");
- for (byte v: byteValues) {
+ if (intValues != null && intValues.length > 0) {
+ sb.append(" int values:");
+ for (int v: intValues) {
sb.append(" " + v);
}
}
diff --git a/carsupport-lib/src/android/support/car/CarSensorManager.java b/carsupport-lib/src/android/support/car/CarSensorManager.java
index e407d730d4..20eafe759f 100644
--- a/carsupport-lib/src/android/support/car/CarSensorManager.java
+++ b/carsupport-lib/src/android/support/car/CarSensorManager.java
@@ -30,6 +30,7 @@ import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.List;
/**
* API for monitoring car sensor data.
@@ -70,25 +71,20 @@ public class CarSensorManager implements CarManagerBase {
public static final int SENSOR_TYPE_FUEL_LEVEL = 5;
/**
* Represents the current status of parking brake. Sensor data in {@link CarSensorEvent} is an
- * int (byteValues[0]). Value of 1 represents parking brake applied while 0 means the other way
+ * intValues[0]. Value of 1 represents parking brake applied while 0 means the other way
* around. For this sensor, rate in {@link #registerListener(CarSensorEventListener, int, int)}
* will be ignored and all changes will be notified.
*/
public static final int SENSOR_TYPE_PARKING_BRAKE = 6;
/**
* This represents the current position of transmission gear. Sensor data in
- * {@link CarSensorEvent} is an int (byteValues[0]). For the meaning of the value, check
+ * {@link CarSensorEvent} is an intValues[0]. For the meaning of the value, check
* {@link CarSensorEvent#GEAR_NEUTRAL} and other GEAR_*.
*/
public static final int SENSOR_TYPE_GEAR = 7;
+ public static final int SENSOR_TYPE_RESERVED8 = 8;
/**
- * Diagnostics message / event coming from car. The data is coming as byte array in
- * {@link CarSensorEvent}.
- * TODO: clarify data format
- */
- public static final int SENSOR_TYPE_DIAGNOSTICS = 8;
- /**
- * Day/night sensor. Sensor data is an int (byteValues[0]).
+ * Day/night sensor. Sensor data is intValues[0].
*/
public static final int SENSOR_TYPE_NIGHT = 9;
/**
@@ -97,7 +93,7 @@ public class CarSensorManager implements CarManagerBase {
public static final int SENSOR_TYPE_LOCATION = 10;
/**
* Represents the current driving status of car. Different user interaction should be used
- * depending on the current driving status. Driving status is an int (byteValues[0]).
+ * depending on the current driving status. Driving status is intValues[0].
*/
public static final int SENSOR_TYPE_DRIVING_STATUS = 11;
/**
@@ -107,24 +103,26 @@ public class CarSensorManager implements CarManagerBase {
/**
* Current status of HVAC system like target temperature and current temperature.
*/
- public static final int SENSOR_TYPE_HVAC = 13;
+ public static final int SENSOR_TYPE_RESERVED13 = 13;
public static final int SENSOR_TYPE_ACCELEROMETER = 14;
- public static final int SENSOR_TYPE_DEAD_RECKONING = 15;
- public static final int SENSOR_TYPE_DOOR = 16;
+ public static final int SENSOR_TYPE_RESERVED15 = 15;
+ public static final int SENSOR_TYPE_RESERVED16 = 16;
public static final int SENSOR_TYPE_GPS_SATELLITE = 17;
public static final int SENSOR_TYPE_GYROSCOPE = 18;
- public static final int SENSOR_TYPE_LIGHT = 19;
- public static final int SENSOR_TYPE_PASSENGER = 20;
- public static final int SENSOR_TYPE_TIRE_PRESSURE = 21;
+ public static final int SENSOR_TYPE_RESERVED19 = 19;
+ public static final int SENSOR_TYPE_RESERVED20 = 20;
+ public static final int SENSOR_TYPE_RESERVED21 = 21;
/** Sensor type bigger than this is invalid. Always update this after adding a new sensor. */
- private static final int SENSOR_TYPE_MAX = SENSOR_TYPE_TIRE_PRESSURE;
+ private static final int SENSOR_TYPE_MAX = SENSOR_TYPE_RESERVED21;
- /** Read sensor in default normal rate set for each sensors. */
+ /** Read sensor in default normal rate set for each sensors. This is default rate. */
public static final int SENSOR_RATE_NORMAL = 3;
- /** Read sensor at the maximum rate. Actual rate will be different depending on the sensor */
+ public static final int SENSOR_RATE_UI = 2;
+ public static final int SENSOR_RATE_FAST = 1;
+ /** Read sensor at the maximum rate. Actual rate will be different depending on the sensor. */
public static final int SENSOR_RATE_FASTEST = 0;
- private static final int MSG_SENSOR_EVENT = 0;
+ private static final int MSG_SENSOR_EVENTS = 0;
private static final int VERSION = 1;
private final ICarSensor mService;
@@ -145,12 +143,15 @@ public class CarSensorManager implements CarManagerBase {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
- case MSG_SENSOR_EVENT:
+ case MSG_SENSOR_EVENTS:
synchronized(mActiveSensorListeners) {
- CarSensorEvent event = (CarSensorEvent) msg.obj;
- CarSensorListeners listeners = mActiveSensorListeners.get(event.sensorType);
- if (listeners != null) {
- listeners.onSensorChanged(event);
+ List<CarSensorEvent> events = (List<CarSensorEvent>) msg.obj;
+ for (CarSensorEvent event: events) {
+ CarSensorListeners listeners =
+ mActiveSensorListeners.get(event.sensorType);
+ if (listeners != null) {
+ listeners.onSensorChanged(event);
+ }
}
}
break;
@@ -403,8 +404,8 @@ public class CarSensorManager implements CarManagerBase {
}
}
- private void handleOnSensorChanged(CarSensorEvent event) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_SENSOR_EVENT, event));
+ private void handleOnSensorChanged(List<CarSensorEvent> events) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_SENSOR_EVENTS, events));
}
private static class CarSensorEventListenerToService extends ICarSensorEventListener.Stub {
@@ -415,10 +416,10 @@ public class CarSensorManager implements CarManagerBase {
}
@Override
- public void onSensorChanged(CarSensorEvent event) {
+ public void onSensorChanged(List<CarSensorEvent> events) {
CarSensorManager manager = mManager.get();
if (manager != null) {
- manager.handleOnSensorChanged(event);
+ manager.handleOnSensorChanged(events);
}
}
}
diff --git a/carsupport-lib/src/android/support/car/ICarSensorEventListener.aidl b/carsupport-lib/src/android/support/car/ICarSensorEventListener.aidl
index 178572ce47..129f9810ab 100644
--- a/carsupport-lib/src/android/support/car/ICarSensorEventListener.aidl
+++ b/carsupport-lib/src/android/support/car/ICarSensorEventListener.aidl
@@ -24,5 +24,5 @@ import android.support.car.CarSensorEvent;
* @hide
*/
oneway interface ICarSensorEventListener {
- void onSensorChanged(in CarSensorEvent event) = 0;
+ void onSensorChanged(in List<CarSensorEvent> events) = 0;
}
diff --git a/service/Android.mk b/service/Android.mk
index c9fa9e1221..45ac914f7c 100644
--- a/service/Android.mk
+++ b/service/Android.mk
@@ -28,10 +28,14 @@ LOCAL_PACKAGE_NAME := CarService
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
-#LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_JNI_SHARED_LIBRARIES := libjni_carservice
+
LOCAL_STATIC_JAVA_LIBRARIES += libcarsupport
include $(BUILD_PACKAGE)
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml
index 957160cf65..65dd57c71c 100644
--- a/service/AndroidManifest.xml
+++ b/service/AndroidManifest.xml
@@ -22,6 +22,37 @@
<original-package android:name="com.android.car" />
+ <permission-group
+ android:name="android.support.car.permission.CAR_INFORMATION"
+ android:icon="@drawable/car_ic_mode"
+ android:description="@string/car_permission_desc"
+ android:label="@string/car_permission_label" />
+
+ <permission
+ android:name="android.support.car.permission.CAR_FUEL"
+ android:permissionGroup="android.support.car.permission.CAR_INFORMATION"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_fuel"
+ android:description="@string/car_permission_desc_fuel" />
+ <permission
+ android:name="android.support.car.permission.CAR_MILEAGE"
+ android:permissionGroup="android.support.car.permission.CAR_INFORMATION"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_mileage"
+ android:description="@string/car_permission_desc_mileage" />
+ <permission
+ android:name="android.support.car.permission.CAR_SPEED"
+ android:permissionGroup="android.permission-group.LOCATION"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_speed"
+ android:description="@string/car_permission_desc_speed" />
+ <permission
+ android:name="android.support.car.permission.CAR_VENDOR_EXTENSION"
+ android:permissionGroup="android.support.car.permission.CAR_INFORMATION"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_vendor_extension"
+ android:description="@string/car_permission_desc_vendor_extension" />
+
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
diff --git a/service/jni/Android.mk b/service/jni/Android.mk
new file mode 100644
index 0000000000..8e25474ead
--- /dev/null
+++ b/service/jni/Android.mk
@@ -0,0 +1,43 @@
+# 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.
+#
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(patsubst ./%,%, $(shell cd $(LOCAL_PATH); \
+ find . -name "*.cpp" -and -not -name ".*"))
+
+LOCAL_C_INCLUDES += \
+ external/libvterm/include \
+ libcore/include \
+ frameworks/base/include
+
+LOCAL_SHARED_LIBRARIES := \
+ libandroidfw \
+ libandroid_runtime \
+ liblog \
+ libnativehelper \
+ libutils \
+ libhardware
+
+LOCAL_CFLAGS := \
+ -Wno-unused-parameter \
+
+LOCAL_MODULE := libjni_carservice
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/service/jni/HandlerThread.cpp b/service/jni/HandlerThread.cpp
new file mode 100644
index 0000000000..2e150712dc
--- /dev/null
+++ b/service/jni/HandlerThread.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#include "HandlerThread.h"
+
+namespace android {
+
+HandlerThread::HandlerThread()
+ : mShouldQuit(false) {
+
+}
+
+HandlerThread::~HandlerThread() {
+ quit();
+}
+
+sp<Looper> HandlerThread::getLooper() {
+ Mutex::Autolock autoLock(mLock);
+ if (mLooper.get() == 0) {
+ mLooperWait.wait(mLock);
+ }
+ return mLooper;
+}
+
+status_t HandlerThread::start(const char* name, int32_t priority, size_t stack) {
+ return run(name, priority, stack);
+}
+
+void HandlerThread::quit() {
+ if (!isRunning()) {
+ return;
+ }
+ sp<Looper> looper = getLooper();
+ mLock.lock();
+ mShouldQuit = true;
+ mLock.unlock();
+ looper->wake();
+ requestExitAndWait();
+}
+
+bool HandlerThread::threadLoop() {
+ mLock.lock();
+ mLooper = Looper::prepare(0);
+ mLooperWait.broadcast();
+ mLock.unlock();
+ while (true) {
+ do {
+ Mutex::Autolock autoLock(mLock);
+ if (mShouldQuit) {
+ return false;
+ }
+ } while (false);
+ mLooper->pollOnce(-1);
+ }
+ return false;
+}
+
+
+};
diff --git a/service/jni/HandlerThread.h b/service/jni/HandlerThread.h
new file mode 100644
index 0000000000..68d862566c
--- /dev/null
+++ b/service/jni/HandlerThread.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef CAR_HANDLER_THREAD_H_
+#define CAR_HANDLER_THREAD_H_
+
+#include <utils/Looper.h>
+#include <utils/threads.h>
+
+namespace android {
+
+/**
+ * Native HandlerThread implementation looking similar to Java version.
+ */
+class HandlerThread : public Thread {
+public:
+ HandlerThread();
+ virtual ~HandlerThread();
+
+ sp<Looper> getLooper();
+ status_t start(const char* name = 0, int32_t priority = PRIORITY_DEFAULT, size_t stack = 0);
+ void quit();
+
+private:
+ bool threadLoop();
+
+private:
+ sp<Looper> mLooper;
+ mutable Mutex mLock;
+ bool mShouldQuit;
+ Condition mLooperWait;
+};
+
+};
+
+#endif /* CAR_HANDLER_THREAD_H_ */
diff --git a/service/jni/IVehicleHalEventListener.h b/service/jni/IVehicleHalEventListener.h
new file mode 100644
index 0000000000..d314efebf4
--- /dev/null
+++ b/service/jni/IVehicleHalEventListener.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef CAR_VEHICLE_HAL_EVENT_LISTENER_H_
+#define CAR_VEHICLE_HAL_EVENT_LISTENER_H_
+
+#include <hardware/vehicle.h>
+
+#include <utils/List.h>
+
+namespace android {
+
+/**
+ * Listener to monitor Hal event. Should register this to VehicleHal instance.
+ */
+class IVehicleHalEventListener {
+public:
+ virtual ~IVehicleHalEventListener() {};
+
+ /**
+ * Called in hal thread context during init. Can be a place to run thread specific init.
+ */
+ virtual void onHalThreadInit() = 0;
+ virtual void onHalThreadRelease() = 0;
+ virtual void onHalEvents(List<vehicle_prop_value_t*>& events) = 0;
+ virtual void onHalError(int errorCode) = 0;
+};
+
+
+};
+#endif /* CAR_VEHICLE_HAL_EVENT_LISTENER_H_ */
diff --git a/service/jni/JniInit.cpp b/service/jni/JniInit.cpp
new file mode 100644
index 0000000000..52edc1cdca
--- /dev/null
+++ b/service/jni/JniInit.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "CAR.JNI"
+
+#include <utils/Log.h>
+#include <jni.h>
+
+namespace android {
+extern int register_com_android_car_hal_VehicleHal(JNIEnv *env);
+};
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
+ JNIEnv* env = NULL;
+ jint result = -1;
+
+ if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+ ALOGE("GetEnv failed!");
+ return result;
+ }
+ ALOG_ASSERT(env, "Could not retrieve the env!");
+
+ int r = android::register_com_android_car_hal_VehicleHal(env);
+ if (r != 0) {
+ ALOGE("register_com_android_car_hal_VehicleHal failed %d", r);
+ return JNI_ERR;
+ }
+ return JNI_VERSION_1_6;
+}
diff --git a/service/jni/VehicleHal.cpp b/service/jni/VehicleHal.cpp
new file mode 100644
index 0000000000..6450fe06ec
--- /dev/null
+++ b/service/jni/VehicleHal.cpp
@@ -0,0 +1,239 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "CAR.HAL"
+
+#include <utils/Errors.h>
+#include <utils/SystemClock.h>
+
+#include "VehicleHal.h"
+
+namespace android {
+
+VehicleHalMessageHandler::VehicleHalMessageHandler(const sp<Looper>& looper,
+ IVehicleHalEventListener& listener)
+ : mLooper(looper),
+ mListener(listener),
+ mFreeListIndex(0),
+ mLastDispatchTime(0) {
+}
+
+VehicleHalMessageHandler::~VehicleHalMessageHandler() {
+
+}
+
+void VehicleHalMessageHandler::handleInit() {
+ Mutex::Autolock autoLock(mLock);
+ mLooper->sendMessage(this, Message(INIT));
+}
+
+void VehicleHalMessageHandler::handleRelease() {
+ Mutex::Autolock autoLock(mLock);
+ mLooper->sendMessage(this, Message(RELEASE));
+ mHalThreadWait.wait(mLock);
+}
+
+static const int MS_TO_NS = 1000000;
+
+void VehicleHalMessageHandler::handleHalEvent(vehicle_prop_value_t *eventData) {
+ Mutex::Autolock autoLock(mLock);
+ List<vehicle_prop_value_t*>& propList = mHalPropertyList[mFreeListIndex];
+ propList.push_back(eventData);
+ int64_t deltaFromLast = elapsedRealtime() - mLastDispatchTime;
+ if (deltaFromLast > DISPATCH_INTERVAL_MS) {
+ mLooper->sendMessage(this, Message(HAL_EVENT));
+ } else {
+ mLooper->sendMessageDelayed((DISPATCH_INTERVAL_MS - deltaFromLast) * MS_TO_NS,
+ this, Message(HAL_EVENT));
+ }
+}
+
+void VehicleHalMessageHandler::handleHalError(int errorCode) {
+ Mutex::Autolock autoLock(mLock);
+ // Do not care about overwriting previous error as any error is critical anyway.
+ mLastError = errorCode;
+ mLooper->sendMessage(this, Message(HAL_ERROR));
+}
+
+void VehicleHalMessageHandler::doHandleInit() {
+ mListener.onHalThreadInit();
+}
+
+void VehicleHalMessageHandler::doHandleRelease() {
+ mListener.onHalThreadRelease();
+ Mutex::Autolock autoLock(mLock);
+ mHalThreadWait.broadcast();
+}
+
+void VehicleHalMessageHandler::doHandleHalEvent() {
+ // event dispatching can take time, so do it outside lock and that requires double buffering.
+ // inside lock, free buffer is swapped with non-free buffer.
+ List<vehicle_prop_value_t*>* events = NULL;
+ do {
+ Mutex::Autolock autoLock(mLock);
+ mLastDispatchTime = elapsedRealtime();
+ int nonFreeListIndex = mFreeListIndex ^ 0x1;
+ List<vehicle_prop_value_t*>* nonFreeList = &(mHalPropertyList[nonFreeListIndex]);
+ List<vehicle_prop_value_t*>* freeList = &(mHalPropertyList[mFreeListIndex]);
+ if (nonFreeList->size() > 0) {
+ for (auto& e : *freeList) {
+ nonFreeList->push_back(e);
+ }
+ freeList->clear();
+ events = nonFreeList;
+ } else if (freeList->size() > 0) {
+ events = freeList;
+ mFreeListIndex = nonFreeListIndex;
+ }
+ } while (false);
+ if (events != NULL) {
+ mListener.onHalEvents(*events);
+ //TODO implement return to memory pool
+ for (auto& e : *events) {
+ delete e;
+ //TODO delete pointer type properly.
+ }
+ events->clear();
+ }
+}
+
+void VehicleHalMessageHandler::doHandleHalError() {
+ Mutex::Autolock autoLock(mLock);
+ mListener.onHalError(mLastError);
+}
+
+void VehicleHalMessageHandler::handleMessage(const Message& message) {
+ switch (message.what) {
+ case INIT:
+ doHandleInit();
+ break;
+ case RELEASE:
+ doHandleRelease();
+ break;
+ case HAL_EVENT:
+ doHandleHalEvent();
+ break;
+ case HAL_ERROR:
+ doHandleHalError();
+ break;
+ }
+}
+
+// store HAL instance for callback
+VehicleHal* VehicleHal::sInstance = NULL;
+
+VehicleHal::VehicleHal(IVehicleHalEventListener& listener)
+ : mListener(listener),
+ mModule(NULL) {
+ //TODO
+ sInstance = this;
+}
+
+VehicleHal::~VehicleHal() {
+ //TODO
+ sInstance = NULL;
+}
+
+int VehicleHal::eventCallback(const vehicle_prop_value_t *eventData) {
+ sInstance->onHalEvent(eventData);
+ return NO_ERROR;
+}
+
+
+int VehicleHal::errorCallback(uint32_t errorCode) {
+ sInstance->onHalError(errorCode);
+ return NO_ERROR;
+}
+
+status_t VehicleHal::init() {
+ Mutex::Autolock autoLock(mLock);
+ status_t r = loadHal();
+ if (r!= NO_ERROR) {
+ ALOGE("cannot load HAL, error:%d", r);
+ return r;
+ }
+ r = mHandlerThread.start("HAL.NATIVE_LOOP");
+ if (r != NO_ERROR) {
+ ALOGE("cannot start handler thread, error:%d", r);
+ return r;
+ }
+ sp<VehicleHalMessageHandler> handler(new VehicleHalMessageHandler(mHandlerThread.getLooper(),
+ mListener));
+ mHandler = handler;
+ mHandler->handleInit();
+ r = mDevice->init(mDevice, eventCallback, errorCallback);
+ if (r != NO_ERROR) {
+ ALOGE("HAL init failed:%d", r);
+ return r;
+ }
+ return NO_ERROR;
+}
+
+void VehicleHal::release() {
+ Mutex::Autolock autoLock(mLock);
+ mDevice->release(mDevice);
+ if (mHandler.get() != NULL) {
+ mHandler->handleRelease();
+ }
+ mHandlerThread.quit();
+}
+
+vehicle_prop_config_t const * VehicleHal::listProperties(int* numConfigs) {
+ return mDevice->list_properties(mDevice, numConfigs);
+}
+
+status_t VehicleHal::getProperty(vehicle_prop_value_t *data) {
+ return mDevice->get(mDevice, data);
+}
+
+status_t VehicleHal::setProperty(vehicle_prop_value_t& data) {
+ return mDevice->set(mDevice, &data);
+}
+
+status_t VehicleHal::subscribe(uint32_t prop, float sampleRate) {
+ return mDevice->subscribe(mDevice, prop, sampleRate);
+}
+
+void VehicleHal::unsubscribe(uint32_t prop) {
+ mDevice->unsubscribe(mDevice, prop);
+}
+
+void VehicleHal::onHalEvent(const vehicle_prop_value_t *eventData) {
+ //TODO add memory pool
+ //TODO handle pointer type data which requires one more alloc / copy
+ vehicle_prop_value_t* copy = new vehicle_prop_value_t();
+ memcpy(copy, eventData, sizeof(vehicle_prop_value_t));
+ mHandler->handleHalEvent(copy);
+}
+
+void VehicleHal::onHalError(int errorCode) {
+ mHandler->handleHalError(errorCode);
+}
+
+status_t VehicleHal::loadHal() {
+ int r = hw_get_module(VEHICLE_HARDWARE_MODULE_ID, (hw_module_t const**)&mModule);
+ if (r != NO_ERROR) {
+ ALOGE("cannot load HAL module, error:%d", r);
+ return r;
+ }
+ r = mModule->common.methods->open(&mModule->common, VEHICLE_HARDWARE_DEVICE,
+ (hw_device_t**)&mDevice);
+ return r;
+}
+
+void VehicleHal::closeHal() {
+ mDevice->common.close(&mDevice->common);
+}
+};
diff --git a/service/jni/VehicleHal.h b/service/jni/VehicleHal.h
new file mode 100644
index 0000000000..6d491a073e
--- /dev/null
+++ b/service/jni/VehicleHal.h
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+#ifndef CAR_VEHICLE_HAL_H_
+#define CAR_VEHICLE_HAL_H_
+
+#include <memory>
+
+#include <hardware/hardware.h>
+#include <hardware/vehicle.h>
+
+#include <utils/threads.h>
+#include <utils/List.h>
+
+#include "HandlerThread.h"
+#include "IVehicleHalEventListener.h"
+
+namespace android {
+
+/**
+ * MessageHandler to dispatch HAL callbacks to pre-defined handler thread context.
+ * Init / release is handled in the handler thread to allow upper layer to allocate resource
+ * for the thread.
+ */
+class VehicleHalMessageHandler : public MessageHandler {
+ enum {
+ INIT = 0,
+ RELEASE = 1,
+ HAL_EVENT = 2,
+ HAL_ERROR = 3,
+ };
+
+ /**
+ * For dispatching HAL event in batch. Hal events coming in this time frame will be batched
+ * together.
+ */
+ static const int DISPATCH_INTERVAL_MS = 16;
+public:
+ VehicleHalMessageHandler(const sp<Looper>& mLooper, IVehicleHalEventListener& listener);
+ virtual ~VehicleHalMessageHandler();
+
+ void handleInit();
+ void handleRelease();
+ void handleHalEvent(vehicle_prop_value_t *eventData);
+ void handleHalError(int errorCode);
+
+private:
+ void handleMessage(const Message& message);
+ void doHandleInit();
+ void doHandleRelease();
+ void doHandleHalEvent();
+ void doHandleHalError();
+
+private:
+ Mutex mLock;
+ Condition mHalThreadWait;
+ sp<Looper> mLooper;
+ IVehicleHalEventListener& mListener;
+ int mLastError;
+ int mFreeListIndex;
+ List<vehicle_prop_value_t*> mHalPropertyList[2];
+ int64_t mLastDispatchTime;
+};
+
+/**
+ * C++ Wrapper for vehicle hal
+ */
+class VehicleHal {
+public:
+ VehicleHal(IVehicleHalEventListener& listener);
+ ~VehicleHal();
+ status_t init();
+ void release();
+ void onHalEvent(const vehicle_prop_value_t *eventData);
+ void onHalError(int errorCode);
+ vehicle_prop_config_t const * listProperties(int* numConfigs);
+ status_t getProperty(vehicle_prop_value_t *data);
+ status_t setProperty(vehicle_prop_value_t& data);
+ status_t subscribe(uint32_t prop, float sample_rate);
+ void unsubscribe(uint32_t prop);
+private:
+ status_t loadHal();
+ void closeHal();
+ static int eventCallback(const vehicle_prop_value_t *eventData);
+ static int errorCallback(uint32_t errorCode);
+private:
+ static VehicleHal* sInstance;
+ HandlerThread mHandlerThread;
+ sp<VehicleHalMessageHandler> mHandler;
+ IVehicleHalEventListener& mListener;
+ mutable Mutex mLock;
+ vehicle_module_t* mModule;
+ vehicle_hw_device_t* mDevice;
+};
+
+};
+
+#endif /* CAR_VEHICLE_HAL_H_ */
diff --git a/service/jni/com_android_car_hal_Hal.cpp b/service/jni/com_android_car_hal_Hal.cpp
new file mode 100644
index 0000000000..cf45e2b0f8
--- /dev/null
+++ b/service/jni/com_android_car_hal_Hal.cpp
@@ -0,0 +1,425 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "CAR.HAL"
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/SystemClock.h>
+#include <jni.h>
+#include <JNIHelp.h>
+
+#include <memory>
+
+#include "VehicleHal.h"
+#include "IVehicleHalEventListener.h"
+
+#define DBG
+
+#ifdef DBG
+#define DBG_LOG(x...) ALOGD(x)
+#else
+#define DBG_LOG(x...)
+#endif
+
+namespace android {
+
+
+static const char* JAVA_HAL_PROPERTY_CLASS_NAME = "com/android/car/hal/HalProperty";
+
+class JniVehicleHal : public IVehicleHalEventListener {
+public:
+ JniVehicleHal(jobject javaHal, jmethodID idOnHalDataEvents, int maxProperties, int maxData,
+ jintArray properties, jlongArray timestamps,
+ jintArray intData, jfloatArray floatData,
+ JavaVM* jvm)
+ : mJavaHal(javaHal),
+ mIdOnHalDataEvents(idOnHalDataEvents),
+ mNumMaxProperties(maxProperties),
+ mNumMaxData(maxData),
+ mProperties(new jint[4 * maxProperties]),
+ mTimeStamps(new jlong[maxProperties]),
+ mIntData(new jint[maxData]),
+ mFloatData(new jfloat[maxData]),
+ mJavaProperties(properties),
+ mJavaTimestamps(timestamps),
+ mJavaIntData(intData),
+ mJavaFloatData(floatData),
+ mHal(*this),
+ mJvm(jvm)
+ {
+ // nothing to do
+ };
+
+ virtual ~JniVehicleHal() {
+ //TODO
+ };
+
+ status_t init() {
+ return mHal.init();
+ };
+
+ void release(JNIEnv* env) {
+ // first stop hal thread.
+ mHal.release();
+ env->DeleteGlobalRef(mJavaProperties);
+ env->DeleteGlobalRef(mJavaTimestamps);
+ env->DeleteGlobalRef(mJavaIntData);
+ env->DeleteGlobalRef(mJavaFloatData);
+ env->DeleteGlobalRef(mJavaHal);
+ };
+
+ jobjectArray getSupportedProperties(JNIEnv *env) {
+ int numProperties = -1;
+ vehicle_prop_config_t const * list = mHal.listProperties(&numProperties);
+ if (numProperties <= 0) {
+ ALOGE("No properties from HAL, error:%d", numProperties);
+ return NULL;
+ }
+ jclass halPropertyCls = env->FindClass(JAVA_HAL_PROPERTY_CLASS_NAME);
+ if (halPropertyCls == NULL) {
+ ALOGE("cannot load class %s", JAVA_HAL_PROPERTY_CLASS_NAME);
+ return NULL;
+ }
+ jmethodID initMethidId =
+ env->GetMethodID(halPropertyCls, "<init>", "(IIIIFF)V");
+ if (initMethidId == NULL) {
+ ALOGE("cannot find constructor for %s", JAVA_HAL_PROPERTY_CLASS_NAME);
+ return NULL;
+ }
+ jobjectArray properties = env->NewObjectArray(numProperties, halPropertyCls, NULL);
+
+ for (int i = 0; i < numProperties; i++) {
+ //TODO add more members
+ int propertyType = list->prop;
+ int dataType = list->value_type;
+ DBG_LOG("New property %x type %x", propertyType, dataType);
+ jint accessType = list->access;
+ jint changeMode = list->change_mode;
+ jfloat minSampleRate = list->min_sample_rate;
+ jfloat maxSampleRate = list->max_sample_rate;
+ jobject prop = env->NewObject(halPropertyCls, initMethidId, propertyType, dataType,
+ accessType, changeMode, minSampleRate, maxSampleRate);
+ env->SetObjectArrayElement(properties, i, prop);
+ env->DeleteLocalRef(prop);
+ list++;
+ }
+ return properties;
+ };
+
+ void dispatchCurrentEvents(int numProperties, int numPropertiesArray, int numIntValues,
+ int numFloatValues) {
+ mJniEnv->SetIntArrayRegion(mJavaProperties, 0, numPropertiesArray, mProperties.get());
+ mJniEnv->SetLongArrayRegion(mJavaTimestamps, 0, numProperties, mTimeStamps.get());
+ mJniEnv->SetIntArrayRegion(mJavaIntData, 0, numIntValues, mIntData.get());
+ mJniEnv->SetFloatArrayRegion(mJavaFloatData, 0, numFloatValues, mFloatData.get());
+ mJniEnv->CallVoidMethod(mJavaHal, mIdOnHalDataEvents, numProperties);
+ }
+
+ void onHalEvents(List<vehicle_prop_value_t*>& events) {
+ DBG_LOG("onHalEvent, num events %d", events.size());
+ int numProperties = 0;
+ int indexPropertiesArray = 0;
+ int indexCurrentIntValues = 0;
+ int indexCurrentFloatValues = 0;
+ for (auto& e : events) {
+ numProperties++;
+ int intSizeToAdd = 0;
+ int floatSizeToAdd = 0;
+ int type = 0;
+ switch(e->value_type) {
+ case VEHICLE_VALUE_TYPE_FLOAT:
+ floatSizeToAdd = 1;
+ break;
+ case VEHICLE_VALUE_TYPE_INT64:
+ intSizeToAdd = 2;
+ break;
+ case VEHICLE_VALUE_TYPE_INT32:
+ case VEHICLE_VALUE_TYPE_BOOLEAN:
+ intSizeToAdd = 1;
+ break;
+ default:
+ ALOGE("onHalEvents type not implemented yet %d", e->value_type);
+ break;
+ /* TODO
+ case VEHICLE_VALUE_TYPE_STRING:
+ case HVAC: */
+ }
+ // One of arrays are full. Need to send upward now.
+ if (((numProperties > mNumMaxProperties) ||
+ ((indexCurrentIntValues + intSizeToAdd) > mNumMaxData) ||
+ (indexCurrentFloatValues + floatSizeToAdd) > mNumMaxData)) {
+ dispatchCurrentEvents(numProperties - 1, indexPropertiesArray,
+ indexCurrentIntValues,
+ indexCurrentFloatValues);
+ numProperties = 1; // including the current one
+ indexPropertiesArray = 0;
+ indexCurrentIntValues = 0;
+ indexCurrentFloatValues = 0;
+ }
+ // fill data now
+ switch(e->value_type) {
+ case VEHICLE_VALUE_TYPE_FLOAT:
+ mFloatData[indexCurrentFloatValues] = e->value.float_value;
+ indexCurrentFloatValues++;
+ break;
+ case VEHICLE_VALUE_TYPE_INT64:
+ mIntData[indexCurrentIntValues] = (e->value.int64_value & 0xffffffff);
+ indexCurrentIntValues++;
+ mIntData[indexCurrentIntValues] = (e->value.int64_value >> 32);
+ indexCurrentIntValues++;
+ break;
+ case VEHICLE_VALUE_TYPE_INT32:
+ case VEHICLE_VALUE_TYPE_BOOLEAN:
+ mIntData[indexCurrentIntValues] = e->value.int32_value;
+ indexCurrentIntValues++;
+ break;
+ default:
+ ALOGE("onHalEvents type not implemented yet %d", e->value_type);
+ break;
+ /* TODO
+ case VEHICLE_VALUE_TYPE_STRING:
+ case HVAC: */
+ }
+ mTimeStamps[numProperties - 1] = e->timestamp;
+ mProperties[indexPropertiesArray] = e->prop;
+ indexPropertiesArray++;
+ mProperties[indexPropertiesArray] = e->value_type;
+ indexPropertiesArray++;
+ mProperties[indexPropertiesArray] = intSizeToAdd;
+ indexPropertiesArray++;
+ mProperties[indexPropertiesArray] = floatSizeToAdd;
+ indexPropertiesArray++;
+ }
+ dispatchCurrentEvents(numProperties, indexPropertiesArray, indexCurrentIntValues,
+ indexCurrentFloatValues);
+ };
+
+ void onHalError(int errorCode) {
+ //TODO
+ }
+
+ void onHalThreadInit() {
+ // called from HAL handler thread.
+ JavaVMAttachArgs args;
+ args.version = JNI_VERSION_1_6;
+ args.name = NULL;
+ args.group = NULL;
+ mJvm->AttachCurrentThread((JNIEnv**)&mJniEnv, &args);
+ }
+
+ void onHalThreadRelease() {
+ mJvm->DetachCurrentThread();
+ }
+
+ inline void fillProperty(vehicle_prop_value_t* propValue, int property, int valueType) {
+ propValue->prop = property;
+ propValue->value_type = valueType;
+ //TODO refine HAL definition to ignore timestamp in set. No need to set this.
+ //propValue->timestamp = elapsedRealtimeNano();
+ }
+
+ status_t setIntProperty(int property, int value) {
+ vehicle_prop_value_t propValue;
+ fillProperty(&propValue, property, VEHICLE_VALUE_TYPE_INT32);
+ propValue.value.int32_value = value;
+ return mHal.setProperty(propValue);
+ }
+
+ status_t getIntProperty(int property, int* value) {
+ vehicle_prop_value_t propValue;
+ //TODO clarify valid items in get, only prop
+ propValue.prop = property;
+ //fillProperty(property, VEHICLE_VALUE_TYPE_SIGNED_INT_32);
+ int r = mHal.getProperty(&propValue);
+ if (r != NO_ERROR) {
+ return r;
+ }
+ if (propValue.value_type != VEHICLE_VALUE_TYPE_INT32) {
+ return BAD_TYPE;
+ }
+ *value = propValue.value.int32_value;
+ return r;
+ }
+
+ status_t setFloatProperty(int property, float value) {
+ vehicle_prop_value_t propValue;
+ fillProperty(&propValue, property, VEHICLE_VALUE_TYPE_FLOAT);
+ propValue.value.float_value = value;
+ return mHal.setProperty(propValue);
+ }
+
+ status_t getFloatProperty(int property, float* value) {
+ vehicle_prop_value_t propValue;
+ //TODO clarify valid items in get, only prop
+ propValue.prop = property;
+ int r = mHal.getProperty(&propValue);
+ if (r != NO_ERROR) {
+ return r;
+ }
+ if (propValue.value_type != VEHICLE_VALUE_TYPE_FLOAT) {
+ return BAD_TYPE;
+ }
+ *value = propValue.value.float_value;
+ return r;
+ }
+
+ status_t subscribeProperty(int property, float sampleRateHz) {
+ return mHal.subscribe(property, sampleRateHz);
+ }
+
+ void unsubscribeProperty(int property) {
+ mHal.unsubscribe(property);
+ }
+
+private:
+ jobject mJavaHal;
+ const jmethodID mIdOnHalDataEvents;
+ const int mNumMaxProperties;
+ const int mNumMaxData;
+ std::unique_ptr<jint[]> mProperties;
+ std::unique_ptr<jlong[]> mTimeStamps;
+ std::unique_ptr<jint[]> mIntData;
+ std::unique_ptr<jfloat[]> mFloatData;
+ jintArray mJavaProperties;
+ jlongArray mJavaTimestamps;
+ jintArray mJavaIntData;
+ jfloatArray mJavaFloatData;
+ VehicleHal mHal;
+ JavaVM* mJvm;
+ JNIEnv* mJniEnv;
+};
+
+static const char* JAVA_RUNTIME_EXCEPTION_CLASS_NAME = "java/lang/RuntimeException";
+
+static jmethodID getMethodID(JNIEnv *env, jclass clz, const char* name, const char* sig) {
+ const jmethodID r = env->GetMethodID(clz, name, sig);
+ if (r == 0) {
+ String8 msg = String8::format("cannot find method %s with signature %s from Java Hal", name,
+ sig);
+ ALOGE("%s", msg.string());
+ env->ThrowNew(env->FindClass(JAVA_RUNTIME_EXCEPTION_CLASS_NAME), msg.string());
+ }
+ return r;
+}
+
+static jlong com_android_car_hal_VehicleHal_nativeInit(JNIEnv *env, jobject javaHal,
+ jintArray properties, jlongArray timestamps, jintArray intData, jfloatArray floatData) {
+ const int maxProperties = env->GetArrayLength(properties);
+ const int maxData = env->GetArrayLength(intData);
+ jclass javaHalCls = env->GetObjectClass(javaHal);
+ const jmethodID idOnHalDataEvents = getMethodID(env, javaHalCls, "onHalDataEvents",
+ "(I)V");
+ if (idOnHalDataEvents == 0) {
+ return 0;
+ }
+ jobject globalJavaHal = env->NewGlobalRef(javaHal);
+ jintArray globalProperties = (jintArray) env->NewGlobalRef(properties);
+ jlongArray globalTimestamps = (jlongArray) env->NewGlobalRef(timestamps);
+ jintArray globalIntData = (jintArray) env->NewGlobalRef(intData);
+ jfloatArray globalFloatData = (jfloatArray) env->NewGlobalRef(floatData);
+ JavaVM* jvm;
+ env->GetJavaVM(&jvm);
+ JniVehicleHal* hal = new JniVehicleHal(
+ globalJavaHal,
+ idOnHalDataEvents,
+ maxProperties,
+ maxData,
+ globalProperties,
+ globalTimestamps,
+ globalIntData,
+ globalFloatData,
+ jvm);
+ status_t r = hal->init();
+ if (r != NO_ERROR) {
+ String8 msg = String8::format("cannot init hal, returned %d", r);
+ env->ThrowNew(env->FindClass(JAVA_RUNTIME_EXCEPTION_CLASS_NAME), msg.string());
+ }
+ return (jlong) hal;
+}
+
+static void com_android_car_hal_VehicleHal_nativeRelease(JNIEnv *env, jobject, jlong jniHal) {
+ JniVehicleHal* hal = reinterpret_cast<JniVehicleHal*>(jniHal);
+ // Little ugly here as all global ref should be released inside release.
+ hal->release(env);
+ delete hal;
+}
+
+static jobjectArray com_android_car_hal_VehicleHal_getSupportedProperties(JNIEnv *env, jobject,
+ jlong jniHal) {
+ JniVehicleHal* hal = reinterpret_cast<JniVehicleHal*>(jniHal);
+ return hal->getSupportedProperties(env);
+}
+
+static void com_android_car_hal_VehicleHal_setIntProperty(JNIEnv *, jobject, jlong jniHal,
+ jint property, jint value) {
+ JniVehicleHal* hal = reinterpret_cast<JniVehicleHal*>(jniHal);
+ hal->setIntProperty(property, value);
+ //TODO check error
+}
+
+static jint com_android_car_hal_VehicleHal_getIntProperty(JNIEnv *, jobject, jlong jniHal,
+ jint property) {
+ JniVehicleHal* hal = reinterpret_cast<JniVehicleHal*>(jniHal);
+ int value = -1;
+ hal->getIntProperty(property, &value);
+ //TODO check error
+ return value;
+}
+
+static void com_android_car_hal_VehicleHal_setFloatProperty(JNIEnv *, jobject, jlong jniHal,
+ jint property, jfloat) {
+ JniVehicleHal* hal = reinterpret_cast<JniVehicleHal*>(jniHal);
+ //TODO
+}
+
+static jfloat com_android_car_hal_VehicleHal_getFloatProperty(JNIEnv *, jobject,
+ jlong jniHal, jint property) {
+ JniVehicleHal* hal = reinterpret_cast<JniVehicleHal*>(jniHal);
+ //TODO
+ return 0;
+}
+
+static int com_android_car_hal_VehicleHal_subscribeProperty(JNIEnv *, jobject, jlong jniHal,
+ jint property, jfloat sampleRateHz) {
+ JniVehicleHal* hal = reinterpret_cast<JniVehicleHal*>(jniHal);
+ return hal->subscribeProperty(property, sampleRateHz);
+}
+
+static void com_android_car_hal_VehicleHal_unsubscribeProperty(JNIEnv *, jobject, jlong jniHal,
+ jint property) {
+ JniVehicleHal* hal = reinterpret_cast<JniVehicleHal*>(jniHal);
+ return hal->unsubscribeProperty(property);
+}
+
+static JNINativeMethod gMethods[] = {
+ { "nativeInit", "([I[J[I[F)J", (void*)com_android_car_hal_VehicleHal_nativeInit },
+ { "nativeRelease", "(J)V", (void*)com_android_car_hal_VehicleHal_nativeRelease },
+ { "getSupportedProperties", "(J)[Lcom/android/car/hal/HalProperty;", (void*)com_android_car_hal_VehicleHal_getSupportedProperties },
+ { "setIntProperty", "(JII)V", (void*)com_android_car_hal_VehicleHal_setIntProperty },
+ { "getIntProperty", "(JI)I", (void*)com_android_car_hal_VehicleHal_getIntProperty },
+ { "setFloatProperty", "(JIF)V", (void*)com_android_car_hal_VehicleHal_setFloatProperty },
+ { "getFloatProperty", "(JI)F", (void*)com_android_car_hal_VehicleHal_getFloatProperty },
+ { "subscribeProperty", "(JIF)I", (void*)com_android_car_hal_VehicleHal_subscribeProperty },
+ { "unsubscribeProperty", "(JI)V", (void*)com_android_car_hal_VehicleHal_unsubscribeProperty },
+};
+
+int register_com_android_car_hal_VehicleHal(JNIEnv *env) {
+ return jniRegisterNativeMethods(env, "com/android/car/hal/VehicleHal",
+ gMethods, NELEM(gMethods));
+}
+};
diff --git a/service/jni/vehicleHalConsts.h b/service/jni/vehicleHalConsts.h
new file mode 100644
index 0000000000..52f36d6de1
--- /dev/null
+++ b/service/jni/vehicleHalConsts.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+//TODO auto-generate this from vehicle.h
+
+namespace android {
+
+#include <hardware/vehicle.h>
+
+vehicle_value_type getPropertyDataType(int property) {
+ switch (property) {
+ case VEHICLE_PROPERTY_INFO_VIN:
+ return VEHICLE_VALUE_TYPE_STRING;
+ case VEHICLE_PROPERTY_INFO_MAKE:
+ return VEHICLE_VALUE_TYPE_STRING;
+ case VEHICLE_PROPERTY_INFO_MODEL:
+ return VEHICLE_VALUE_TYPE_STRING;
+ case VEHICLE_PROPERTY_INFO_MODEL_YEAR:
+ return VEHICLE_VALUE_TYPE_INT32;
+ case VEHICLE_PROPERTY_PERF_VEHICLE_SPEED:
+ return VEHICLE_VALUE_TYPE_FLOAT;
+ case VEHICLE_PROPERTY_GEAR_SELECTION:
+ return VEHICLE_VALUE_TYPE_INT32;
+ case VEHICLE_PROPERTY_CURRENT_GEAR:
+ return VEHICLE_VALUE_TYPE_INT32;
+ case VEHICLE_PROPERTY_PARKING_BRAKE_ON:
+ return VEHICLE_VALUE_TYPE_BOOLEAN;
+ case VEHICLE_PROPERTY_DRIVE_STATE:
+ return VEHICLE_VALUE_TYPE_INT32;
+ case VEHICLE_PROPERTY_NIGHT_MODE:
+ return VEHICLE_VALUE_TYPE_BOOLEAN;
+ }
+ return VEHICLE_VALUE_TYPE_SHOUD_NOT_USE;
+}
+
+};
diff --git a/service/res/drawable-hdpi/car_ic_mode.png b/service/res/drawable-hdpi/car_ic_mode.png
new file mode 100644
index 0000000000..a8f719fd9e
--- /dev/null
+++ b/service/res/drawable-hdpi/car_ic_mode.png
Binary files differ
diff --git a/service/res/drawable-mdpi/car_ic_mode.png b/service/res/drawable-mdpi/car_ic_mode.png
new file mode 100644
index 0000000000..38a97479d4
--- /dev/null
+++ b/service/res/drawable-mdpi/car_ic_mode.png
Binary files differ
diff --git a/service/res/drawable-xhdpi/car_ic_mode.png b/service/res/drawable-xhdpi/car_ic_mode.png
new file mode 100644
index 0000000000..58a1aca66f
--- /dev/null
+++ b/service/res/drawable-xhdpi/car_ic_mode.png
Binary files differ
diff --git a/service/res/drawable-xxhdpi/car_ic_mode.png b/service/res/drawable-xxhdpi/car_ic_mode.png
new file mode 100644
index 0000000000..e82cdd4c95
--- /dev/null
+++ b/service/res/drawable-xxhdpi/car_ic_mode.png
Binary files differ
diff --git a/service/res/values/strings.xml b/service/res/values/strings.xml
new file mode 100644
index 0000000000..17f5fb2bf7
--- /dev/null
+++ b/service/res/values/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+ <!-- For permissions -->
+ <!-- Permission text: can access your car's information [CHAR LIMIT=NONE] -->
+ <string name="car_permission_label">Car information</string>
+ <!-- Permission text: can access your car's information [CHAR LIMIT=NONE] -->
+ <string name="car_permission_desc">Access your car\'s information.</string>
+ <!-- Permission text: can access your car's fuel level [CHAR LIMIT=NONE] -->
+ <string name="car_permission_label_fuel">car fuel level</string>
+ <!-- Permission text: can access your car's fuel level [CHAR LIMIT=NONE] -->
+ <string name="car_permission_desc_fuel">Access your car\'s fuel level information.</string>
+ <!-- Permission text: can access your car's mileage information [CHAR LIMIT=NONE] -->
+ <string name="car_permission_label_mileage">car mileage</string>
+ <!-- Permission text: can access your car's mileage information [CHAR LIMIT=NONE] -->
+ <string name="car_permission_desc_mileage">Access your car\'s mileage information.</string>
+ <!-- Permission text: can access your car's speed [CHAR LIMIT=NONE] -->
+ <string name="car_permission_label_speed">car speed</string>
+ <!-- Permission text: can access your car's speed [CHAR LIMIT=NONE] -->
+ <string name="car_permission_desc_speed">Access your car\'s speed.</string>
+ <!-- Permission text: apps can access car-manufacturer specific data [CHAR LIMIT=NONE] -->
+ <string name="car_permission_label_vendor_extension">car vendor channel</string>
+ <!-- Permission text: apps can access car-manufacturer specific data [CHAR LIMIT=NONE] -->
+ <string name="car_permission_desc_vendor_extension">Access your car\'s vendor channel to
+ exchange car-specific information.</string>
+</resources> \ No newline at end of file
diff --git a/service/src/com/android/car/BootReceiver.java b/service/src/com/android/car/BootReceiver.java
index 5bfa96c3a1..c13a1f3b39 100644
--- a/service/src/com/android/car/BootReceiver.java
+++ b/service/src/com/android/car/BootReceiver.java
@@ -23,7 +23,7 @@ import android.content.Context;
import android.content.Intent;
import android.util.Log;
-import com.android.car.hal.Hal;
+import com.android.car.hal.VehicleHal;
/**
@@ -34,7 +34,7 @@ public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.w(CarLog.TAG_SERVICE, "Starting...");
- Hal hal = Hal.getInstance(context.getApplicationContext());
+ VehicleHal hal = VehicleHal.getInstance(context.getApplicationContext());
Intent carServiceintent = new Intent();
carServiceintent.setPackage(context.getPackageName());
carServiceintent.setAction(Car.CAR_SERVICE_INTERFACE_NAME);
diff --git a/service/src/com/android/car/CarLog.java b/service/src/com/android/car/CarLog.java
index f8bdff9c9d..bd6d718e59 100644
--- a/service/src/com/android/car/CarLog.java
+++ b/service/src/com/android/car/CarLog.java
@@ -20,4 +20,5 @@ public class CarLog {
public static final String TAG_SERVICE = "CAR.SERVICE";
public static final String TAG_SENSOR = "CAR.SENSOR";
+ public static final String TAG_HAL = "CAR.HAL";
}
diff --git a/service/src/com/android/car/CarSensorEventFactory.java b/service/src/com/android/car/CarSensorEventFactory.java
new file mode 100644
index 0000000000..35c29fa8fc
--- /dev/null
+++ b/service/src/com/android/car/CarSensorEventFactory.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+import android.support.car.CarSensorEvent;
+
+//TODO add memory pool and recycling
+public class CarSensorEventFactory {
+
+ public static CarSensorEvent createBooleanEvent(int sensorType, long timeStampNs,
+ boolean value) {
+ CarSensorEvent event = new CarSensorEvent(sensorType, timeStampNs, 0, 1);
+ event.intValues[0] = value ? 1 : 0;
+ return event;
+ }
+
+ public static CarSensorEvent createIntEvent(int sensorType, long timeStampNs, int value) {
+ CarSensorEvent event = new CarSensorEvent(sensorType, timeStampNs, 0, 1);
+ event.intValues[0] = value;
+ return event;
+ }
+
+ public static CarSensorEvent createFloatEvent(int sensorType, long timeStampNs, float value) {
+ CarSensorEvent event = new CarSensorEvent(sensorType, timeStampNs, 1, 0);
+ event.floatValues[0] = value;
+ return event;
+ }
+
+ public static void returnToPool(CarSensorEvent event) {
+ //TODO
+ }
+}
diff --git a/service/src/com/android/car/CarSensorService.java b/service/src/com/android/car/CarSensorService.java
index cd899af68d..d28f838ebe 100644
--- a/service/src/com/android/car/CarSensorService.java
+++ b/service/src/com/android/car/CarSensorService.java
@@ -19,8 +19,13 @@ package com.android.car;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.hardware.SensorEvent;
import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -29,37 +34,61 @@ import android.support.car.CarSensorEvent;
import android.support.car.CarSensorManager;
import android.support.car.ICarSensor;
import android.support.car.ICarSensorEventListener;
+import android.support.car.CarSensorManager.CarSensorEventListener;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
-import com.android.car.hal.Hal;
-import com.android.car.hal.SensorHal;
-import com.android.car.hal.SensorHalBase;
-import com.android.car.hal.SensorHalBase.SensorListener;
+import com.android.car.hal.VehicleHal;
+import com.android.car.hal.HalProperty;
+import com.android.car.hal.SensorHalService;
+import com.android.car.hal.SensorHalServiceBase;
+import com.android.car.hal.SensorHalServiceBase.SensorListener;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.ConcurrentModificationException;
+import java.util.HashMap;
import java.util.LinkedList;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
public class CarSensorService extends ICarSensor.Stub
- implements CarServiceBase, SensorHal.SensorListener {
+ implements CarServiceBase, SensorHalService.SensorListener {
/**
* Abstraction for logical sensor which is not physical sensor but presented as sensor to
* upper layer. Currently {@link CarSensorManager#SENSOR_TYPE_NIGHT} and
* {@link CarSensorManager#SENSOR_TYPE_DRIVING_STATUS} falls into this category.
* Implementation can call {@link CarSensorService#onSensorData(CarSensorEvent)} when there
- * is state change for the given sensor after {@link SensorHalBase#init()}
+ * is state change for the given sensor after {@link SensorHalServiceBase#init()}
* is called.
*/
- public static abstract class LogicalSensorHalBase extends SensorHalBase {
+ public static abstract class LogicalSensorHalBase extends SensorHalServiceBase {
+
+ @Override
+ public void handleBooleanHalEvent(int property, boolean value, long timeStamp) {
+ //no-op default version as logical sensor does not come from HAL.
+ }
+
+ @Override
+ public void handleIntHalEvent(int property, int value, long timeStamp) {
+ //no-op
+ }
+
+ @Override
+ public void handleFloatHalEvent(int property, float value, long timeStamp) {
+ //no-op
+ }
+
+ @Override
+ public List<HalProperty> takeSupportedProperties(List<HalProperty> allProperties) {
+ return null;
+ }
/** Sensor service is ready and all vehicle sensors are available. */
public abstract void onSensorServiceReady();
@@ -90,7 +119,7 @@ public class CarSensorService extends ICarSensor.Stub
@GuardedBy("mSensorLock")
private final SparseArray<SensorRecord> mSensorRecords = new SparseArray<>();
- private final SensorHal mSensorHal;
+ private final SensorHalService mSensorHal;
private int[] mCarProvidedSensors;
private int[] mSupportedSensors;
private final AtomicBoolean mSensorDiscovered = new AtomicBoolean(false);
@@ -98,12 +127,20 @@ public class CarSensorService extends ICarSensor.Stub
private final Context mContext;
private final DrivingStatePolicy mDrivingStatePolicy;
+ private boolean mUseDefaultDrivingPolicy = true;
private final DayNightModePolicy mDayNightModePolicy;
+ private boolean mUseDefaultDayNightModePolicy = true;
+
+ private final HandlerThread mHandlerThread;
+ private final SensorDispatchHandler mSensorDispatchHandler;
public CarSensorService(Context context) {
mContext = context;
+ mHandlerThread = new HandlerThread("SENSOR", Process.THREAD_PRIORITY_AUDIO);
+ mHandlerThread.start();
+ mSensorDispatchHandler = new SensorDispatchHandler(mHandlerThread.getLooper());
// This triggers sensor hal init as well.
- mSensorHal = Hal.getInstance(context.getApplicationContext()).getSensorHal();
+ mSensorHal = VehicleHal.getInstance(context.getApplicationContext()).getSensorHal();
mDrivingStatePolicy = new DrivingStatePolicy(context);
mDayNightModePolicy = new DayNightModePolicy(context);
}
@@ -112,23 +149,27 @@ public class CarSensorService extends ICarSensor.Stub
public void init() {
// Watch out the order. registerSensorListener can lead into onSensorHalReady call.
// So it should be done last.
- mDrivingStatePolicy.init();
- mDayNightModePolicy.init();
mSensorLock.lock();
try {
mSupportedSensors = refreshSupportedSensorsLocked();
- addNewSensorRecordLocked(CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
- mDrivingStatePolicy.getDefaultValue(
- CarSensorManager.SENSOR_TYPE_DRIVING_STATUS));
- addNewSensorRecordLocked(CarSensorManager.SENSOR_TYPE_NIGHT,
- mDrivingStatePolicy.getDefaultValue(
- CarSensorManager.SENSOR_TYPE_NIGHT));
+ if (mUseDefaultDrivingPolicy) {
+ mDrivingStatePolicy.init();
+ addNewSensorRecordLocked(CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
+ mDrivingStatePolicy.getDefaultValue(
+ CarSensorManager.SENSOR_TYPE_DRIVING_STATUS));
+ mDrivingStatePolicy.registerSensorListener(this);
+ }
+ if (mUseDefaultDayNightModePolicy) {
+ mDayNightModePolicy.init();
+ addNewSensorRecordLocked(CarSensorManager.SENSOR_TYPE_NIGHT,
+ mDrivingStatePolicy.getDefaultValue(
+ CarSensorManager.SENSOR_TYPE_NIGHT));
+ mDayNightModePolicy.registerSensorListener(this);
+ }
} finally {
mSensorLock.unlock();
}
- mDrivingStatePolicy.registerSensorListener(this);
- mDayNightModePolicy.registerSensorListener(this);
mSensorHal.registerSensorListener(this);
}
@@ -140,10 +181,15 @@ public class CarSensorService extends ICarSensor.Stub
@Override
public void release() {
- mDrivingStatePolicy.release();
- mDayNightModePolicy.release();
+ mHandlerThread.quit();
tryHoldSensorLock();
try {
+ if (mUseDefaultDrivingPolicy) {
+ mDrivingStatePolicy.release();
+ }
+ if (mUseDefaultDayNightModePolicy) {
+ mDayNightModePolicy.release();
+ }
for (int i = mSensorListeners.size() - 1; i >= 0; --i) {
SensorListeners listener = mSensorListeners.valueAt(i);
listener.release();
@@ -171,7 +217,7 @@ public class CarSensorService extends ICarSensor.Stub
}
@Override
- public void onSensorHalReady(SensorHalBase hal) {
+ public void onSensorHalReady(SensorHalServiceBase hal) {
if (hal == mSensorHal) {
mCarProvidedSensors = mSensorHal.getSupportedSensors();
if (Log.isLoggable(CarLog.TAG_SENSOR, Log.VERBOSE)) {
@@ -181,27 +227,42 @@ public class CarSensorService extends ICarSensor.Stub
mSensorLock.lock();
try {
mSupportedSensors = refreshSupportedSensorsLocked();
+ if (mUseDefaultDrivingPolicy) {
+ mDrivingStatePolicy.onSensorServiceReady();
+ }
+ if (mUseDefaultDayNightModePolicy) {
+ mDayNightModePolicy.onSensorServiceReady();
+ }
} finally {
mSensorLock.unlock();
}
- mDrivingStatePolicy.onSensorServiceReady();
- mDayNightModePolicy.onSensorServiceReady();
}
}
- private void processSensorDataLocked(CarSensorEvent event) {
- SensorRecord record = mSensorRecords.get(event.sensorType);
- if (record != null) {
- record.lastEvent = event;
- } else {
- if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
- Log.d(CarLog.TAG_SENSOR, "sensor data received without matching record");
+ private void processSensorData(List<CarSensorEvent> events) {
+ mSensorLock.lock();
+ for (CarSensorEvent event: events) {
+ SensorRecord record = mSensorRecords.get(event.sensorType);
+ if (record != null) {
+ if (record.lastEvent == null) {
+ record.lastEvent = event;
+ } else if (record.lastEvent.timeStampNs < event.timeStampNs) {
+ record.lastEvent = event;
+ //TODO recycle event
+ } else { // wrong timestamp, throw away this.
+ //TODO recycle new event
+ continue;
+ }
+ SensorListeners listeners = mSensorListeners.get(event.sensorType);
+ if (listeners != null) {
+ listeners.queueSensorEvent(event);
+ }
}
}
- if (Log.isLoggable(CarLog.TAG_SENSOR, Log.VERBOSE)) {
- Log.v(CarLog.TAG_SENSOR, "onSensorData type: " + event.sensorType);
+ for (SensorClient client: mClients) {
+ client.dispatchSensorUpdate();
}
- notifySensorEventToClientLocked(event, event.sensorType);
+ mSensorLock.unlock();
}
/**
@@ -210,13 +271,13 @@ public class CarSensorService extends ICarSensor.Stub
* @param event
*/
@Override
- public void onSensorData(CarSensorEvent event) {
- mSensorLock.lock();
- try {
- processSensorDataLocked(event);
- } finally {
- mSensorLock.unlock();
- }
+ public void onSensorEvents(List<CarSensorEvent> events) {
+ mSensorDispatchHandler.handleSensorEvents(events);
+ }
+
+ @Override
+ public void onSensorEvent(CarSensorEvent event) {
+ mSensorDispatchHandler.handleSensorEvent(event);
}
@Override
@@ -280,12 +341,13 @@ public class CarSensorService extends ICarSensor.Stub
// If we have a cached event for this sensor, send the event.
SensorRecord record = mSensorRecords.get(sensorType);
if (record != null && record.lastEvent != null) {
- sensorClient.onSensorUpdate(record.lastEvent);
+ sensorClient.queueSensorEvent(record.lastEvent);
+ sensorClient.dispatchSensorUpdate();
}
if (sensorListeners == null) {
sensorListeners = new SensorListeners(rate);
mSensorListeners.put(sensorType, sensorListeners);
- shouldStartSensors = isSensorRealLocked(sensorType);
+ shouldStartSensors = true;
} else {
oldRate = Integer.valueOf(sensorListeners.getRate());
sensorClientWithRate = sensorListeners.findSensorClientWithRate(sensorClient);
@@ -298,7 +360,7 @@ public class CarSensorService extends ICarSensor.Stub
}
if (sensorListeners.getRate() > rate) {
sensorListeners.setRate(rate);
- shouldStartSensors = isSensorRealLocked(sensorType);
+ shouldStartSensors = sensorSupportRate(sensorType);
}
sensorClient.addSensor(sensorType);
} finally {
@@ -325,6 +387,30 @@ public class CarSensorService extends ICarSensor.Stub
return true;
}
+ private boolean sensorSupportRate(int sensorType) {
+ switch (sensorType) {
+ case CarSensorManager.SENSOR_TYPE_COMPASS:
+ case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
+ case CarSensorManager.SENSOR_TYPE_RPM:
+ case CarSensorManager.SENSOR_TYPE_LOCATION:
+ case CarSensorManager.SENSOR_TYPE_ACCELEROMETER:
+ case CarSensorManager.SENSOR_TYPE_GPS_SATELLITE:
+ case CarSensorManager.SENSOR_TYPE_GYROSCOPE:
+ return true;
+ case CarSensorManager.SENSOR_TYPE_ODOMETER:
+ case CarSensorManager.SENSOR_TYPE_FUEL_LEVEL:
+ case CarSensorManager.SENSOR_TYPE_PARKING_BRAKE:
+ case CarSensorManager.SENSOR_TYPE_GEAR:
+ case CarSensorManager.SENSOR_TYPE_NIGHT:
+ case CarSensorManager.SENSOR_TYPE_DRIVING_STATUS:
+ case CarSensorManager.SENSOR_TYPE_ENVIRONMENT:
+ return false;
+ default:
+ Log.w(CarLog.TAG_SENSOR, "sensorSupportRate not listed sensor:" + sensorType);
+ return false;
+ }
+ }
+
private int getSensorPermission(int sensorType) {
String permission = getPermissionName(sensorType);
int result = PackageManager.PERMISSION_GRANTED;
@@ -365,7 +451,7 @@ public class CarSensorService extends ICarSensor.Stub
if (Log.isLoggable(CarLog.TAG_SENSOR, Log.VERBOSE)) {
Log.v(CarLog.TAG_SENSOR, "startSensor " + sensorType + " with rate " + rate);
}
- SensorHalBase sensorHal = getSensorHal(sensorType);
+ SensorHalServiceBase sensorHal = getSensorHal(sensorType);
if (sensorHal != null) {
if (!sensorHal.isReady()) {
Log.w(CarLog.TAG_SENSOR, "Sensor channel not available.");
@@ -430,11 +516,11 @@ public class CarSensorService extends ICarSensor.Stub
}
sensorListeners.removeSensorClientWithRate(clientWithRate);
if (sensorListeners.getNumberOfClients() == 0) {
- shouldStopSensor = isSensorRealLocked(sensorType);
+ shouldStopSensor = true;
mSensorListeners.remove(sensorType);
} else if (sensorListeners.updateRate()) { // rate changed
newRate = sensorListeners.getRate();
- shouldRestartSensor = isSensorRealLocked(sensorType);
+ shouldRestartSensor = sensorSupportRate(sensorType);
}
if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
Log.d(CarLog.TAG_SENSOR, "unregister succeeded");
@@ -453,7 +539,7 @@ public class CarSensorService extends ICarSensor.Stub
if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
Log.d(CarLog.TAG_SENSOR, "stopSensor " + sensorType);
}
- SensorHalBase sensorHal = getSensorHal(sensorType);
+ SensorHalServiceBase sensorHal = getSensorHal(sensorType);
if (sensorHal == null || !sensorHal.isReady()) {
Log.w(CarLog.TAG_SENSOR, "Sensor channel not available.");
return;
@@ -471,14 +557,24 @@ public class CarSensorService extends ICarSensor.Stub
sensorHal.requestSensorStop(sensorType);
}
- private SensorHalBase getSensorHal(int sensorType) {
- switch (sensorType) {
- case CarSensorManager.SENSOR_TYPE_DRIVING_STATUS:
- return mDrivingStatePolicy;
- case CarSensorManager.SENSOR_TYPE_NIGHT:
- return mDayNightModePolicy;
- default:
- return mSensorHal;
+ private SensorHalServiceBase getSensorHal(int sensorType) {
+ try {
+ mSensorLock.lock();
+ switch (sensorType) {
+ case CarSensorManager.SENSOR_TYPE_DRIVING_STATUS:
+ if (mUseDefaultDrivingPolicy) {
+ return mDrivingStatePolicy;
+ }
+ break;
+ case CarSensorManager.SENSOR_TYPE_NIGHT:
+ if (mUseDefaultDayNightModePolicy) {
+ return mDayNightModePolicy;
+ }
+ break;
+ }
+ return mSensorHal;
+ } finally {
+ mSensorLock.unlock();
}
}
@@ -499,16 +595,36 @@ public class CarSensorService extends ICarSensor.Stub
private int[] refreshSupportedSensorsLocked() {
int numCarSensors = (mCarProvidedSensors == null) ? 0 : mCarProvidedSensors.length;
+ for (int i = 0; i < numCarSensors; i++) {
+ int sensor = mCarProvidedSensors[i];
+ if (sensor == CarSensorManager.SENSOR_TYPE_DRIVING_STATUS) {
+ mUseDefaultDrivingPolicy = false;
+ } else if (sensor == CarSensorManager.SENSOR_TYPE_NIGHT) {
+ mUseDefaultDayNightModePolicy = false;
+ }
+ }
+ int totalNumSensors = numCarSensors;
+ if (mUseDefaultDrivingPolicy) {
+ totalNumSensors++;
+ }
+ if (mUseDefaultDayNightModePolicy) {
+ totalNumSensors++;
+ }
// Two logical sensors are always added.
- int[] supportedSensors = new int[numCarSensors + 2];
+ int[] supportedSensors = new int[totalNumSensors];
int index = 0;
- supportedSensors[index] = CarSensorManager.SENSOR_TYPE_DRIVING_STATUS;
- index++;
- supportedSensors[index] = CarSensorManager.SENSOR_TYPE_NIGHT;
- index++;
+ if (mUseDefaultDrivingPolicy) {
+ supportedSensors[index] = CarSensorManager.SENSOR_TYPE_DRIVING_STATUS;
+ index++;
+ }
+ if (mUseDefaultDayNightModePolicy) {
+ supportedSensors[index] = CarSensorManager.SENSOR_TYPE_NIGHT;
+ index++;
+ }
for (int i = 0; i < numCarSensors; i++) {
int sensor = mCarProvidedSensors[i];
+
if (mSensorRecords.get(sensor) == null) {
SensorRecord record = new SensorRecord();
mSensorRecords.put(sensor, record);
@@ -531,15 +647,6 @@ public class CarSensorService extends ICarSensor.Stub
return false;
}
- private void notifySensorEventToClientLocked(CarSensorEvent event, int sensor) {
- SensorListeners listeners = mSensorListeners.get(sensor);
- if (listeners != null) {
- listeners.onSensorUpdate(event);
- } else {
- Log.w(CarLog.TAG_SENSOR, "sensor event while no listener, sensor:" + sensor);
- }
- }
-
/**
* Find SensorClient from client list and return it.
* This should be called with mClients locked.
@@ -569,11 +676,91 @@ public class CarSensorService extends ICarSensor.Stub
}
}
+ private class SensorDispatchHandler extends Handler {
+ private static final long SENSOR_DISPATCH_MIN_INTERVAL_MS = 16; // over 60Hz
+
+ private static final int MSG_SENSOR_DATA = 0;
+
+ private long mLastSensorDispatchTime = -1;
+ private int mFreeListIndex = 0;
+ private final LinkedList<CarSensorEvent>[] mSensorDataList = new LinkedList[2];
+
+ private SensorDispatchHandler(Looper looper) {
+ super(looper);
+ for (int i = 0; i < mSensorDataList.length; i++) {
+ mSensorDataList[i] = new LinkedList<CarSensorEvent>();
+ }
+ }
+
+ private synchronized void handleSensorEvents(List<CarSensorEvent> data) {
+ LinkedList<CarSensorEvent> list = mSensorDataList[mFreeListIndex];
+ list.addAll(data);
+ requestDispatchLocked();
+ }
+
+ private synchronized void handleSensorEvent(CarSensorEvent event) {
+ LinkedList<CarSensorEvent> list = mSensorDataList[mFreeListIndex];
+ list.add(event);
+ requestDispatchLocked();
+ }
+
+ private void requestDispatchLocked() {
+ Message msg = obtainMessage(MSG_SENSOR_DATA);
+ long now = SystemClock.uptimeMillis();
+ long delta = now - mLastSensorDispatchTime;
+ if (delta > SENSOR_DISPATCH_MIN_INTERVAL_MS) {
+ sendMessage(msg);
+ } else {
+ sendMessageDelayed(msg, SENSOR_DISPATCH_MIN_INTERVAL_MS - delta);
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SENSOR_DATA:
+ doHandleSensorData();
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void doHandleSensorData() {
+ List<CarSensorEvent> listToDispatch = null;
+ synchronized (this) {
+ mLastSensorDispatchTime = SystemClock.uptimeMillis();
+ int nonFreeListIndex = mFreeListIndex ^ 0x1;
+ List<CarSensorEvent> nonFreeList = mSensorDataList[nonFreeListIndex];
+ List<CarSensorEvent> freeList = mSensorDataList[mFreeListIndex];
+ if (nonFreeList.size() > 0) {
+ Log.w(CarLog.TAG_SENSOR, "non free list not empty");
+ // copy again, but this should not be normal case
+ nonFreeList.addAll(freeList);
+ listToDispatch = nonFreeList;
+ freeList.clear();
+ } else if (freeList.size() > 0) {
+ listToDispatch = freeList;
+ mFreeListIndex = nonFreeListIndex;
+ }
+ }
+ // leave this part outside lock so that time-taking dispatching can be done without
+ // blocking sensor event notification.
+ if (listToDispatch != null) {
+ processSensorData(listToDispatch);
+ listToDispatch.clear();
+ }
+ }
+
+ }
+
/** internal instance for pending client request */
private class SensorClient implements IBinder.DeathRecipient {
/** callback for sensor events */
private final ICarSensorEventListener mListener;
private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
+ private final LinkedList<CarSensorEvent> mSensorsToDispatch =
+ new LinkedList<CarSensorEvent>();
/** when false, it is already released */
private volatile boolean mActive = true;
@@ -628,24 +815,33 @@ public class CarSensorService extends ICarSensor.Stub
removeClient(this);
}
- void onSensorUpdate(CarSensorEvent event) {
- try {
- if (mActive) {
- mListener.onSensorChanged(event);
- } else {
- if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
- Log.d(CarLog.TAG_SENSOR, "sensor update while client is already released");
- }
+ void queueSensorEvent(CarSensorEvent event) {
+ mSensorsToDispatch.add(event);
+ }
+
+ void dispatchSensorUpdate() {
+ if (mSensorsToDispatch.size() == 0) {
+ return;
+ }
+ if (mActive) {
+ try {
+ mListener.onSensorChanged(mSensorsToDispatch);
+ } catch (RemoteException e) {
+ //ignore. crash will be handled by death handler
+ }
+ } else {
+ if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
+ Log.d(CarLog.TAG_SENSOR, "sensor update while client is already released");
}
- } catch (RemoteException e) {
- //ignore. crash will be handled by death handler
}
+ mSensorsToDispatch.clear();
}
void release() {
if (mActive) {
mListener.asBinder().unlinkToDeath(this, 0);
mActiveSensors.clear();
+ mSensorsToDispatch.clear();
mActive = false;
}
}
@@ -745,12 +941,9 @@ public class CarSensorService extends ICarSensor.Stub
return null;
}
- void onSensorUpdate(CarSensorEvent event) {
- if (Log.isLoggable(CarLog.TAG_SENSOR, Log.VERBOSE)) {
- Log.v(CarLog.TAG_SENSOR, "onSensorUpdate to clients: " + mSensorClients.size());
- }
+ void queueSensorEvent(CarSensorEvent event) {
for (SensorClientWithRate clientWithRate: mSensorClients) {
- clientWithRate.getSensorClient().onSensorUpdate(event);
+ clientWithRate.getSensorClient().queueSensorEvent(event);
}
}
@@ -777,7 +970,6 @@ public class CarSensorService extends ICarSensor.Stub
+ " active: " + record.enabled);
// only print sensor data which is not related with location
if (sensor != CarSensorManager.SENSOR_TYPE_LOCATION &&
- sensor != CarSensorManager.SENSOR_TYPE_DEAD_RECKONING &&
sensor != CarSensorManager.SENSOR_TYPE_GPS_SATELLITE) {
writer.println(" " + record.lastEvent.toString());
}
@@ -826,8 +1018,12 @@ public class CarSensorService extends ICarSensor.Stub
writer.println("concurrent modification happened");
}
writer.println("**driving policy**");
- mDrivingStatePolicy.dump(writer);
+ if (mUseDefaultDrivingPolicy) {
+ mDrivingStatePolicy.dump(writer);
+ }
writer.println("**day/night policy**");
- mDayNightModePolicy.dump(writer);
+ if (mUseDefaultDayNightModePolicy) {
+ mDayNightModePolicy.dump(writer);
+ }
}
}
diff --git a/service/src/com/android/car/CarService.java b/service/src/com/android/car/CarService.java
index 32e47ff230..a342e05ba9 100644
--- a/service/src/com/android/car/CarService.java
+++ b/service/src/com/android/car/CarService.java
@@ -18,6 +18,8 @@ package com.android.car;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import com.android.car.hal.VehicleHal;
+
import android.app.Service;
import android.support.car.Car;
import android.content.Intent;
@@ -57,5 +59,9 @@ public class CarService extends Service {
@Override
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
writer.println("*dump car service*");
+ writer.println("*dump HAL*");
+ VehicleHal.getInstance(getApplicationContext()).dump(writer);
+ writer.println("*dump services*");
+ ICarImpl.getInstance(this).dump(writer);
}
}
diff --git a/service/src/com/android/car/CarServiceApplication.java b/service/src/com/android/car/CarServiceApplication.java
new file mode 100644
index 0000000000..49cd8f07f0
--- /dev/null
+++ b/service/src/com/android/car/CarServiceApplication.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+import android.app.Application;
+
+public class CarServiceApplication extends Application {
+
+ @Override
+ public void onCreate() {
+ // TODO Auto-generated method stub
+ super.onCreate();
+ }
+
+ @Override
+ public void onTerminate() {
+ // TODO Auto-generated method stub
+ super.onTerminate();
+ }
+
+}
diff --git a/service/src/com/android/car/DayNightModePolicy.java b/service/src/com/android/car/DayNightModePolicy.java
index 2a9c426745..23f36d9ed7 100644
--- a/service/src/com/android/car/DayNightModePolicy.java
+++ b/service/src/com/android/car/DayNightModePolicy.java
@@ -21,7 +21,7 @@ import android.support.car.Car;
import android.support.car.CarSensorEvent;
import android.support.car.CarSensorManager;
-import com.android.car.hal.SensorHalBase.SensorListener;
+import com.android.car.hal.SensorHalServiceBase.SensorListener;
import java.io.PrintWriter;
diff --git a/service/src/com/android/car/DrivingStatePolicy.java b/service/src/com/android/car/DrivingStatePolicy.java
index de91318d62..107b228b76 100644
--- a/service/src/com/android/car/DrivingStatePolicy.java
+++ b/service/src/com/android/car/DrivingStatePolicy.java
@@ -24,9 +24,11 @@ import android.support.car.CarSensorManager;
import android.support.car.ICarSensorEventListener;
import android.util.Log;
-import com.android.car.hal.SensorHalBase.SensorListener;
+import com.android.car.hal.SensorHalServiceBase.SensorListener;
import java.io.PrintWriter;
+import java.util.List;
+
/**
* Logical sensor implementing driving state policy. This policy sets only two states:
@@ -47,8 +49,10 @@ public class DrivingStatePolicy extends CarSensorService.LogicalSensorHalBase {
private final ICarSensorEventListener mICarSensorEventListener =
new ICarSensorEventListener.Stub() {
@Override
- public void onSensorChanged(CarSensorEvent event) {
- handleSensorEvent(event);
+ public void onSensorChanged(List<CarSensorEvent> events) {
+ for (CarSensorEvent event: events) {
+ handleSensorEvent(event);
+ }
}
};
@@ -118,7 +122,7 @@ public class DrivingStatePolicy extends CarSensorService.LogicalSensorHalBase {
@Override
public synchronized boolean requestSensorStart(int sensorType, int rate) {
mStarted = true;
- mSensorListener.onSensorData(createEvent(mDringState));
+ mSensorListener.onSensorEvent(createEvent(mDringState));
return true;
}
@@ -149,7 +153,7 @@ public class DrivingStatePolicy extends CarSensorService.LogicalSensorHalBase {
int drivingState = recalcDrivingStateLocked();
if (drivingState != mDringState && mSensorListener != null) {
mDringState = drivingState;
- mSensorListener.onSensorData(createEvent(mDringState));
+ mSensorListener.onSensorEvent(createEvent(mDringState));
}
break;
default:
@@ -184,24 +188,24 @@ public class DrivingStatePolicy extends CarSensorService.LogicalSensorHalBase {
}
private boolean isParkingBrakeApplied(CarSensorEvent event) {
- return event.byteValues[0] == 1;
+ return event.intValues[0] == 1;
}
private boolean isGearInParkingOrNeutral(CarSensorEvent event) {
- byte gear = event.byteValues[0];
- return (gear == ((byte) CarSensorEvent.GEAR_NEUTRAL & 0xff)) ||
- (gear == ((byte) CarSensorEvent.GEAR_PARK & 0xff));
+ int gear = event.intValues[0];
+ return (gear == CarSensorEvent.GEAR_NEUTRAL) ||
+ (gear == CarSensorEvent.GEAR_PARK);
}
private boolean isGearInParking(CarSensorEvent event) {
- byte gear = event.byteValues[0];
- return gear == ((byte) CarSensorEvent.GEAR_PARK & 0xff);
+ int gear = event.intValues[0];
+ return gear == CarSensorEvent.GEAR_PARK;
}
private CarSensorEvent createEvent(int drivingState) {
CarSensorEvent event = new CarSensorEvent(CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
SystemClock.elapsedRealtimeNanos(), 0, 1);
- event.byteValues[0] = (byte) (drivingState & 0xff);
+ event.intValues[0] = drivingState;
return event;
}
}
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 081a33afc5..598ac8d50a 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -16,6 +16,8 @@
package com.android.car;
+import java.io.PrintWriter;
+
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
@@ -27,7 +29,7 @@ import android.support.car.ICarConnectionListener;
import android.support.car.ICarSensor;
import android.util.Log;
-import com.android.car.hal.Hal;
+import com.android.car.hal.VehicleHal;
import com.android.internal.annotations.GuardedBy;
public class ICarImpl extends ICar.Stub {
@@ -37,7 +39,7 @@ public class ICarImpl extends ICar.Stub {
private static ICarImpl sInstance = null;
private final Context mContext;
- private final Hal mHal;
+ private final VehicleHal mHal;
private final CarSensorService mCarSensorService;
@@ -59,7 +61,7 @@ public class ICarImpl extends ICar.Stub {
public ICarImpl(Context serviceContext) {
mContext = serviceContext;
- mHal = Hal.getInstance(serviceContext.getApplicationContext());
+ mHal = VehicleHal.getInstance(serviceContext.getApplicationContext());
mCarSensorService = new CarSensorService(serviceContext);
}
@@ -69,7 +71,7 @@ public class ICarImpl extends ICar.Stub {
private void release() {
mCarSensorService.release();
- Hal.releaseInstance();
+ VehicleHal.releaseInstance();
}
@Override
@@ -125,4 +127,9 @@ public class ICarImpl extends ICar.Stub {
//TODO
return false;
}
+
+ void dump(PrintWriter writer) {
+ writer.println("**CarSensorService");
+ mCarSensorService.dump(writer);
+ }
}
diff --git a/service/src/com/android/car/hal/Hal.java b/service/src/com/android/car/hal/Hal.java
deleted file mode 100644
index 58e4319eac..0000000000
--- a/service/src/com/android/car/hal/Hal.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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 android.content.Context;
-
-/**
- * Abstraction for vehicle HAL.
- */
-public class Hal {
-
- private final SensorHal mSensorHal;
-
- private static Hal sInstance;
-
- public static synchronized Hal getInstance(Context applicationContext) {
- if (sInstance == null) {
- sInstance = new Hal(applicationContext);
- sInstance.init();
- }
- return sInstance;
- }
-
- public static synchronized void releaseInstance() {
- if (sInstance != null) {
- sInstance.release();
- sInstance = null;
- }
- }
-
- public Hal(Context applicationContext) {
- mSensorHal = new SensorHal();
- }
-
- public void init() {
- mSensorHal.init();
- }
-
- public void release() {
- mSensorHal.release();
- }
-
- public SensorHal getSensorHal() {
- return mSensorHal;
- }
-}
diff --git a/service/src/com/android/car/hal/HalProperty.java b/service/src/com/android/car/hal/HalProperty.java
new file mode 100644
index 0000000000..fe7024628d
--- /dev/null
+++ b/service/src/com/android/car/hal/HalProperty.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+/**
+ * Store configuration information for each HAL property.
+ */
+public class HalProperty {
+ //TODO add all missing items
+ public final int propertyType;
+ public final int dataType;
+ public final int accessType;
+ public final int changeMode;
+ public final float minSampleRate;
+ public final float maxSampleRate;
+
+ public HalProperty(int propertyType, int dataType, int accessType, int changeMode,
+ float minSampleRate, float maxSampleRate) {
+ this.propertyType = propertyType;
+ this.dataType = dataType;
+ this.accessType = accessType;
+ this.changeMode = changeMode;
+ this.minSampleRate = minSampleRate;
+ this.maxSampleRate = maxSampleRate;
+ }
+
+ @Override
+ public String toString() {
+ return "property:" + Integer.toHexString(propertyType) +
+ " data type:" + Integer.toHexString(dataType) +
+ " access type:" + Integer.toHexString(accessType) +
+ " change mode:" + Integer.toHexString(changeMode) +
+ " min sample rate:" + minSampleRate + " max sample rate:" + maxSampleRate;
+ }
+}
diff --git a/service/src/com/android/car/hal/HalPropertyConst.java b/service/src/com/android/car/hal/HalPropertyConst.java
new file mode 100644
index 0000000000..1616644a92
--- /dev/null
+++ b/service/src/com/android/car/hal/HalPropertyConst.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+//TODO Auto generate from vehicle.h
+package com.android.car.hal;
+
+public class HalPropertyConst {
+ public static final int VEHICLE_PROPERTY_INFO_VIN = 0x00000100;
+ public static final int VEHICLE_PROPERTY_INFO_MAKE = 0x00000101;
+ public static final int VEHICLE_PROPERTY_INFO_MODEL = 0x00000102;
+ public static final int VEHICLE_PROPERTY_INFO_MODEL_YEAR = 0x00000103;
+ public static final int VEHICLE_PROPERTY_PERF_VEHICLE_SPEED = 0x00000207;
+ public static final int VEHICLE_PROPERTY_GEAR_SELECTION = 0x00000400;
+ public static final int VEHICLE_PROPERTY_CURRENT_GEAR = 0x00000401;
+ public static final int VEHICLE_PROPERTY_PARKING_BRAKE_ON = 0x00000402;
+ public static final int VEHICLE_PROPERTY_DRIVING_STATUS = 0x00000404;
+ public static final int VEHICLE_PROPERTY_NIGHT_MODE = 0x00000407;
+
+ public static class VehicleValueType {
+ public static final int VEHICLE_VALUE_TYPE_SHOUD_NOT_USE = 0x00;
+ public static final int VEHICLE_VALUE_TYPE_STRING = 0x01;
+ public static final int VEHICLE_VALUE_TYPE_FLOAT = 0x02;
+ public static final int VEHICLE_VALUE_TYPE_INT64 = 0x03;
+ public static final int VEHICLE_VALUE_TYPE_INT32 = 0x04;
+ public static final int VEHICLE_VALUE_TYPE_BOOLEAN = 0x05;
+ public static final int VEHICLE_VALUE_TYPE_HVAC_FAN_SPEED = 0x06;
+ public static final int VEHICLE_VALUE_TYPE_HVAC_FAN_DIRECTION = 0x07;
+ public static final int VEHICLE_VALUE_TYPE_HVAC_ZONE_TEMPERATURE = 0x08;
+ public static final int VEHICLE_VALUE_TYPE_HVAC_DEFROST_ON = 0x09;
+ public static final int VEHICLE_VALUE_TYPE_HVAC_AC_ON = 0x0A;
+ }
+
+ public static class VehiclePropChangeMode {
+ public static final int VEHICLE_PROP_CHANGE_MODE_STATIC = 0x00;
+ public static final int VEHICLE_PROP_CHANGE_MODE_ON_CHANGE = 0x01;
+ public static final int VEHICLE_PROP_CHANGE_MODE_CONTINUOUS = 0x02;
+ }
+
+ public static class VehiclePropAccess {
+ public static final int PROP_ACCESS_READ = 0x01;
+ public static final int PROP_ACCESS_WRITE = 0x02;
+ public static final int PROP_ACCESS_READ_WRITE = 0x03;
+ }
+
+ public int getVehicleValueType(int property) {
+ switch (property) {
+ case VEHICLE_PROPERTY_INFO_VIN:
+ return VehicleValueType.VEHICLE_VALUE_TYPE_STRING;
+ case VEHICLE_PROPERTY_INFO_MAKE:
+ return VehicleValueType.VEHICLE_VALUE_TYPE_STRING;
+ case VEHICLE_PROPERTY_INFO_MODEL:
+ return VehicleValueType.VEHICLE_VALUE_TYPE_STRING;
+ case VEHICLE_PROPERTY_INFO_MODEL_YEAR:
+ return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
+ case VEHICLE_PROPERTY_PERF_VEHICLE_SPEED:
+ return VehicleValueType.VEHICLE_VALUE_TYPE_FLOAT;
+ case VEHICLE_PROPERTY_GEAR_SELECTION:
+ return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
+ case VEHICLE_PROPERTY_CURRENT_GEAR:
+ return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
+ case VEHICLE_PROPERTY_PARKING_BRAKE_ON:
+ return VehicleValueType.VEHICLE_VALUE_TYPE_BOOLEAN;
+ case VEHICLE_PROPERTY_DRIVING_STATUS:
+ return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
+ case VEHICLE_PROPERTY_NIGHT_MODE:
+ return VehicleValueType.VEHICLE_VALUE_TYPE_BOOLEAN;
+ }
+ return VehicleValueType.VEHICLE_VALUE_TYPE_SHOUD_NOT_USE;
+ }
+}
diff --git a/service/src/com/android/car/hal/HalServiceBase.java b/service/src/com/android/car/hal/HalServiceBase.java
new file mode 100644
index 0000000000..680b5b826b
--- /dev/null
+++ b/service/src/com/android/car/hal/HalServiceBase.java
@@ -0,0 +1,52 @@
+/*
+ * 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 java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Common interface for all HAL service like sensor HAL.
+ * Each HAL service is connected with XyzService supporting XyzManager,
+ * and will translate HAL data into car api specific format.
+ */
+public abstract class HalServiceBase {
+ /** initialize */
+ public abstract void init();
+
+ /** release and stop operation */
+ public abstract void release();
+
+ /**
+ * return supported properties among all properties.
+ * @return null if no properties are supported
+ */
+ /**
+ * Take supported properties from given allProperties and return List of supported properties.
+ * @param allProperties
+ * @return null if no properties are supported.
+ */
+ public abstract List<HalProperty> takeSupportedProperties(List<HalProperty> allProperties);
+
+ public abstract void handleBooleanHalEvent(int property, boolean value, long timeStamp);
+
+ public abstract void handleIntHalEvent(int property, int value, long timeStamp);
+
+ public abstract void handleFloatHalEvent(int property, float value, long timeStamp);
+
+ public abstract void dump(PrintWriter writer);
+}
diff --git a/service/src/com/android/car/hal/SensorHal.java b/service/src/com/android/car/hal/SensorHal.java
deleted file mode 100644
index bc8fd5c1d1..0000000000
--- a/service/src/com/android/car/hal/SensorHal.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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 java.io.PrintWriter;
-
-/**
- * TODO
- * Sensor HAL implementation for physical sensors in car.
- */
-public class SensorHal extends SensorHalBase {
-
- private boolean mIsReady = false;
-
- @Override
- public synchronized void init() {
- //TODO
- mIsReady = true;
- }
-
- @Override
- public synchronized void release() {
- //TODO
- }
-
- @Override
- public synchronized void registerSensorListener(SensorHalBase.SensorListener listener) {
- //TODO
- if (mIsReady) {
- listener.onSensorHalReady(this);
- }
- }
-
- @Override
- public synchronized boolean isReady() {
- //TODO
- return true;
- }
-
- @Override
- public synchronized int[] getSupportedSensors() {
- //TODO
- return null;
- }
-
- @Override
- public synchronized boolean requestSensorStart(int sensorType, int rate) {
- //TODO
- return false;
- }
-
- @Override
- public synchronized void requestSensorStop(int sensorType) {
- //TODO
- }
-
- @Override
- public void dump(PrintWriter writer) {
- // TODO Auto-generated method stub
- }
-}
diff --git a/service/src/com/android/car/hal/SensorHalService.java b/service/src/com/android/car/hal/SensorHalService.java
new file mode 100644
index 0000000000..50edeede25
--- /dev/null
+++ b/service/src/com/android/car/hal/SensorHalService.java
@@ -0,0 +1,267 @@
+/*
+ * 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 android.support.car.CarSensorEvent;
+import android.support.car.CarSensorManager;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.car.CarLog;
+import com.android.car.CarSensorEventFactory;
+
+import java.io.PrintWriter;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Sensor HAL implementation for physical sensors in car.
+ */
+public class SensorHalService extends SensorHalServiceBase {
+
+ private static final boolean DBG_EVENTS = true;
+
+ private static final int SENSOR_TYPE_INVALD = -1;
+
+ private final VehicleHal mHal;
+ private boolean mIsReady = false;
+ private SensorHalServiceBase.SensorListener mSensorListener;
+ private final SparseArray<HalProperty> mSensorToHalProperty = new SparseArray<HalProperty>();
+
+ public SensorHalService(VehicleHal hal) {
+ mHal = hal;
+ }
+
+ @Override
+ public synchronized void init() {
+ //TODO
+ mIsReady = true;
+ }
+
+ @Override
+ public synchronized List<HalProperty> takeSupportedProperties(List<HalProperty> allProperties) {
+ LinkedList<HalProperty> supportedProperties = new LinkedList<HalProperty>();
+ for (HalProperty halProperty : allProperties) {
+ int sensor = getSensorTypeFromHalProperty(halProperty.propertyType);
+ if (sensor != SENSOR_TYPE_INVALD &&
+ halProperty.changeMode !=
+ HalPropertyConst.VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_STATIC &&
+ (halProperty.accessType == HalPropertyConst.VehiclePropAccess.PROP_ACCESS_READ
+ || halProperty.accessType ==
+ HalPropertyConst.VehiclePropAccess.PROP_ACCESS_WRITE)) {
+ supportedProperties.add(halProperty);
+ mSensorToHalProperty.append(sensor, halProperty);
+ }
+ }
+ return supportedProperties;
+ }
+
+ @Override
+ public synchronized void release() {
+ mSensorToHalProperty.clear();
+ mIsReady = false;
+ }
+
+ @Override
+ public void handleBooleanHalEvent(int property, boolean value, long timeStamp) {
+ if (DBG_EVENTS) {
+ Log.i(CarLog.TAG_SENSOR, "boolean event, property:" + property + " value:" + value);
+ }
+ switch (property) {
+ case HalPropertyConst.VEHICLE_PROPERTY_NIGHT_MODE:
+ case HalPropertyConst.VEHICLE_PROPERTY_PARKING_BRAKE_ON:
+ break;
+ default:
+ throw new RuntimeException("handleBooleanHalEvent wrong property " + property);
+ }
+ int sensorType = getSensorTypeFromHalProperty(property);
+ if (sensorType == SENSOR_TYPE_INVALD) {
+ throw new RuntimeException("handleBooleanHalEvent no sensor defined for property " +
+ property);
+ }
+ CarSensorEvent event = CarSensorEventFactory.createBooleanEvent(sensorType, timeStamp,
+ value);
+ SensorHalServiceBase.SensorListener sensorListener = null;
+ synchronized (this) {
+ sensorListener = mSensorListener;
+ }
+ sensorListener.onSensorEvent(event);
+ }
+
+ @Override
+ public void handleIntHalEvent(int property, int value, long timeStamp) {
+ if (DBG_EVENTS) {
+ Log.i(CarLog.TAG_SENSOR, "int event, property:" + property + " value:" + value);
+ }
+ switch (property) {
+ case HalPropertyConst.VEHICLE_PROPERTY_GEAR_SELECTION:
+ case HalPropertyConst.VEHICLE_PROPERTY_DRIVING_STATUS:
+ break;
+ default:
+ throw new RuntimeException("handleIntHalEvent wrong property " + property);
+ }
+ int sensorType = getSensorTypeFromHalProperty(property);
+ if (sensorType == SENSOR_TYPE_INVALD) {
+ throw new RuntimeException("handleIntHalEvent no sensor defined for property " +
+ property);
+ }
+ CarSensorEvent event = CarSensorEventFactory.createIntEvent(sensorType, timeStamp, value);
+ SensorHalServiceBase.SensorListener sensorListener = null;
+ synchronized (this) {
+ sensorListener = mSensorListener;
+ }
+ sensorListener.onSensorEvent(event);
+ }
+
+ @Override
+ public void handleFloatHalEvent(int property, float value, long timeStamp) {
+ if (DBG_EVENTS) {
+ Log.i(CarLog.TAG_SENSOR, "float event, property:" + property + " value:" + value);
+ }
+ switch (property) {
+ case HalPropertyConst.VEHICLE_PROPERTY_PERF_VEHICLE_SPEED:
+ break;
+ default:
+ throw new RuntimeException("handleFloatHalEvent wrong property " + property);
+ }
+ int sensorType = getSensorTypeFromHalProperty(property);
+ if (sensorType == SENSOR_TYPE_INVALD) {
+ throw new RuntimeException("handleFloatHalEvent no sensor defined for property " +
+ property);
+ }
+ CarSensorEvent event = CarSensorEventFactory.createFloatEvent(sensorType, timeStamp, value);
+ SensorHalServiceBase.SensorListener sensorListener = null;
+ synchronized (this) {
+ sensorListener = mSensorListener;
+ }
+ sensorListener.onSensorEvent(event);
+ }
+
+ @Override
+ public synchronized void registerSensorListener(SensorHalServiceBase.SensorListener listener) {
+ mSensorListener = listener;
+ if (mIsReady) {
+ listener.onSensorHalReady(this);
+ }
+ }
+
+ @Override
+ public synchronized boolean isReady() {
+ return mIsReady;
+ }
+
+ @Override
+ public synchronized int[] getSupportedSensors() {
+ int[] supportedSensors = new int[mSensorToHalProperty.size()];
+ for (int i = 0; i < supportedSensors.length; i++) {
+ supportedSensors[i] = mSensorToHalProperty.keyAt(i);
+ }
+ return supportedSensors;
+ }
+
+ @Override
+ public synchronized boolean requestSensorStart(int sensorType, int rate) {
+ HalProperty halProp = mSensorToHalProperty.get(sensorType);
+ if (halProp == null) {
+ return false;
+ }
+ //TODO calculate sampling rate properly
+ int r = mHal.subscribeProperty(halProp.propertyType,
+ fixSamplingRateForProperty(halProp, rate));
+ return r == 0;
+ }
+
+ private float fixSamplingRateForProperty(HalProperty prop, int carSensorManagerRate) {
+ if (prop.changeMode ==
+ HalPropertyConst.VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE) {
+ return 0;
+ }
+ float rate = 1.0f;
+ switch (carSensorManagerRate) {
+ case CarSensorManager.SENSOR_RATE_FASTEST:
+ case CarSensorManager.SENSOR_RATE_FAST:
+ rate = 10f;
+ break;
+ case CarSensorManager.SENSOR_RATE_UI:
+ rate = 5f;
+ break;
+ default: // fall back to default.
+ break;
+ }
+ if (rate > prop.maxSampleRate) {
+ rate = prop.maxSampleRate;
+ }
+ if (rate < prop.minSampleRate) {
+ rate = prop.minSampleRate;
+ }
+ return rate;
+ }
+
+ @Override
+ public synchronized void requestSensorStop(int sensorType) {
+ HalProperty halProp = mSensorToHalProperty.get(sensorType);
+ if (halProp == null) {
+ return;
+ }
+ mHal.unsubscribeProperty(halProp.propertyType);
+ }
+
+ public synchronized void onSensorEvents(List<CarSensorEvent> events) {
+ if (mSensorListener != null) {
+ mSensorListener.onSensorEvents(events);
+ }
+ }
+
+ public synchronized void onSensorEvent(CarSensorEvent event) {
+ if (mSensorListener != null) {
+ mSensorListener.onSensorEvent(event);
+ }
+ }
+
+ /**
+ * Covert hal property to sensor type. This is also used to check if specific property
+ * is supported by sensor hal or not.
+ * @param halPropertyType
+ * @return
+ */
+ static int getSensorTypeFromHalProperty(int halPropertyType) {
+ switch (halPropertyType) {
+ case HalPropertyConst.VEHICLE_PROPERTY_PERF_VEHICLE_SPEED:
+ return CarSensorManager.SENSOR_TYPE_CAR_SPEED;
+ case HalPropertyConst.VEHICLE_PROPERTY_GEAR_SELECTION:
+ return CarSensorManager.SENSOR_TYPE_GEAR;
+ case HalPropertyConst.VEHICLE_PROPERTY_NIGHT_MODE:
+ return CarSensorManager.SENSOR_TYPE_NIGHT;
+ case HalPropertyConst.VEHICLE_PROPERTY_PARKING_BRAKE_ON:
+ return CarSensorManager.SENSOR_TYPE_PARKING_BRAKE;
+ case HalPropertyConst.VEHICLE_PROPERTY_DRIVING_STATUS:
+ return CarSensorManager.SENSOR_TYPE_DRIVING_STATUS;
+ default:
+ Log.e(CarLog.TAG_SENSOR, "unknown sensor property from HAL " + halPropertyType);
+ return SENSOR_TYPE_INVALD;
+ }
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+ writer.println("***Sensor HAL***");
+ writer.println("****Supported properties****");
+ for (int i = 0; i < mSensorToHalProperty.size(); i++) {
+ writer.println(mSensorToHalProperty.valueAt(i).toString());
+ }
+ }
+}
diff --git a/service/src/com/android/car/hal/SensorHalBase.java b/service/src/com/android/car/hal/SensorHalServiceBase.java
index 12331b2b1c..cf6e20ab24 100644
--- a/service/src/com/android/car/hal/SensorHalBase.java
+++ b/service/src/com/android/car/hal/SensorHalServiceBase.java
@@ -1,8 +1,25 @@
+/*
+ * 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 android.support.car.CarSensorEvent;
import java.io.PrintWriter;
+import java.util.List;
/**
@@ -12,30 +29,29 @@ import java.io.PrintWriter;
* It is ok to report sensor data {@link SensorListener#onSensorData(CarSensorEvent)} inside
* the {@link #requestSensorStart(int, int)} call.
*/
-public abstract class SensorHalBase {
+public abstract class SensorHalServiceBase extends HalServiceBase {
/**
* Listener for monitoring sensor event. Only sensor service will implement this.
*/
public interface SensorListener {
/**
* Sensor Hal is ready and is fully accessible.
- * This will be called after {@link SensorHalBase#init()}.
+ * This will be called after {@link SensorHalServiceBase#init()}.
*/
- void onSensorHalReady(SensorHalBase hal);
+ void onSensorHalReady(SensorHalServiceBase hal);
/**
- * Sensor data is available.
+ * Sensor events are available.
+ * @param events
+ */
+ void onSensorEvents(List<CarSensorEvent> events);
+
+ /**
+ * A sensor event is available.
* @param event
*/
- void onSensorData(CarSensorEvent event);
+ void onSensorEvent(CarSensorEvent event);
}
- /**
- * Do necessary initialization. After this, {@link #getSupportedSensors()} should work.
- */
- public abstract void init();
-
- public abstract void release();
-
public abstract void registerSensorListener(SensorListener listener);
/**
@@ -44,11 +60,13 @@ public abstract class SensorHalBase {
*/
public abstract boolean isReady();
+ /**
+ * This should work after {@link #init()}.
+ * @return
+ */
public abstract int[] getSupportedSensors();
public abstract boolean requestSensorStart(int sensorType, int rate);
public abstract void requestSensorStop(int sensorType);
-
- public abstract void dump(PrintWriter writer);
}
diff --git a/service/src/com/android/car/hal/VehicleHal.java b/service/src/com/android/car/hal/VehicleHal.java
new file mode 100644
index 0000000000..68daf682c6
--- /dev/null
+++ b/service/src/com/android/car/hal/VehicleHal.java
@@ -0,0 +1,295 @@
+/*
+ * 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 android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.support.car.CarSensorEvent;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.car.CarLog;
+
+import java.io.PrintWriter;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * 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 {
+
+ private static final boolean DBG = true;
+
+ /**
+ * Interface for mocking Hal which is used for testing, but should be kept even in release.
+ */
+ public interface HalMock {
+ //TODO
+ };
+
+ static {
+ System.loadLibrary("jni_carservice");
+ }
+
+ private static VehicleHal sInstance;
+
+ private final HandlerThread mHandlerThread;
+ private final DefaultHandler mDefaultHandler;
+
+ public static synchronized VehicleHal getInstance(Context applicationContext) {
+ if (sInstance == null) {
+ sInstance = new VehicleHal(applicationContext);
+ // init is handled in a separate thread to prevent blocking the calling thread for too
+ // long.
+ sInstance.startInit();
+ }
+ return sInstance;
+ }
+
+ public static synchronized void releaseInstance() {
+ if (sInstance != null) {
+ sInstance.release();
+ sInstance = null;
+ }
+ }
+
+ private final SensorHalService mSensorHal;
+ @SuppressWarnings({"UnusedDeclaration"})
+ private long mNativePtr; // used by native code
+
+ private static final int NUM_MAX_PROPERTY_ENTRIES = 100;
+ private static final int NUM_MAX_DATA_ENTRIES = 1000;
+ /**
+ * Contain property - data type - int length - float length. Hal will fill this array
+ * before calling {@link #onHalDataEvents(int)}.
+ */
+ private final int[] mNativepPropertyInfos = new int[4 * NUM_MAX_PROPERTY_ENTRIES];
+ /**
+ * Contains timestamp for all events. Hal will fill this in native side.
+ */
+ private final long[] mNativeTimestampsNs = new long[NUM_MAX_PROPERTY_ENTRIES];
+ /**
+ * Contain int data passed for HAL events.
+ */
+ private final int[] mNativeIntData = new int[NUM_MAX_DATA_ENTRIES];
+ private final float[] mNativeFloatData = new float[NUM_MAX_DATA_ENTRIES];
+
+ /** stores handler for each HAL property. Property events are sent to handler. */
+ private final SparseArray<HalServiceBase> mPropertyHandlers = new SparseArray<HalServiceBase>();
+ /** This is for iterating all HalServices with fixed order. */
+ private final HalServiceBase[] mAllServices;
+
+ private VehicleHal(Context applicationContext) {
+ mHandlerThread = new HandlerThread("HAL");
+ mHandlerThread.start();
+ mDefaultHandler = new DefaultHandler(mHandlerThread.getLooper());
+ // passing this should be safe as long as it is just kept and not used in constructor
+ mSensorHal = new SensorHalService(this);
+ mAllServices = new HalServiceBase[] { mSensorHal };
+ }
+
+ private void startInit() {
+ mDefaultHandler.requestInit();
+ }
+
+ private void doInit() {
+ mNativePtr = nativeInit(mNativepPropertyInfos, mNativeTimestampsNs, mNativeIntData,
+ mNativeFloatData);
+ HalProperty[] properties = getSupportedProperties(mNativePtr);
+ LinkedList<HalProperty> allProperties = new LinkedList<HalProperty>();
+ for (HalProperty prop : properties) {
+ allProperties.add(prop);
+ if (DBG) {
+ Log.i(CarLog.TAG_HAL, "property: " + prop);
+ }
+ }
+ for (HalServiceBase service: mAllServices) {
+ List<HalProperty> taken = service.takeSupportedProperties(allProperties);
+ if (DBG) {
+ Log.i(CarLog.TAG_HAL, "HalService " + service + " take properties " + taken.size());
+ }
+ for (HalProperty p: taken) {
+ mPropertyHandlers.append(p.propertyType, service);
+ }
+ allProperties.removeAll(taken);
+ service.init();
+ }
+ }
+
+ private void release() {
+ // release in reverse order from init
+ for (int i = mAllServices.length - 1; i >= 0; i--) {
+ mAllServices[i].release();
+ }
+ mHandlerThread.quit();
+ nativeRelease(mNativePtr);
+ }
+
+ public SensorHalService getSensorHal() {
+ return mSensorHal;
+ }
+
+ /**
+ * Start mocking HAL with given mock. Actual H/W will be stop functioning until mocking is
+ * stopped. The call will be blocked until all pending events are delivered to upper layer.
+ */
+ public void startHalMocking(HalMock mock) {
+ //TODO
+ }
+
+ public void stopHalMocking() {
+ //TODO
+ }
+
+ public int subscribeProperty(int property, float samplingRateHz) {
+ return subscribeProperty(mNativePtr, property, samplingRateHz);
+ }
+
+ public void unsubscribeProperty(int property) {
+ unsubscribeProperty(mNativePtr, property);
+ }
+
+ /**
+ * Notification from Hal layer for incoming Hal events. This version allows passing multiple
+ * events in one call to reduce JNI overhead. Native side should combine multiple events if
+ * possible to reduce overhead. The end result is more like simpler serialization but it should
+ * not be as expensive as full serialization like protobuf and this should reduce JNI overhead
+ * a lot.
+ * @param numEvents Number of events passed this time.
+ */
+ @SuppressWarnings({"UnusedDeclaration"})
+ private void onHalDataEvents(int numEvents) {
+ int propertyInfoIndex = 0;
+ int intDataIndex = 0;
+ int floatDataIndex = 0;
+ for (int i = 0; i < numEvents; i++) {
+ int property = mNativepPropertyInfos[propertyInfoIndex];
+ int dataType = mNativepPropertyInfos[propertyInfoIndex + 1];
+ int intLength = mNativepPropertyInfos[propertyInfoIndex + 2];
+ int floatLength = mNativepPropertyInfos[propertyInfoIndex + 3];
+ long timeStamp = mNativeTimestampsNs[i];
+ propertyInfoIndex += 4;
+ HalServiceBase service = mPropertyHandlers.get(property);
+ if (service == null) {
+ Log.e(CarLog.TAG_HAL, "No service for event:" + property);
+ intDataIndex += intLength;
+ floatDataIndex += floatLength;
+ continue;
+ }
+ switch (dataType) {
+ case HalPropertyConst.VehicleValueType.VEHICLE_VALUE_TYPE_BOOLEAN:
+ if (intLength != 1 && floatLength != 0) {
+ Log.e(CarLog.TAG_HAL, "Wrong data type");
+ } else {
+ boolean value = mNativeIntData[intDataIndex] == 1;
+ service.handleBooleanHalEvent(property, value, timeStamp);
+ }
+ break;
+ case HalPropertyConst.VehicleValueType.VEHICLE_VALUE_TYPE_INT32:
+ if (intLength != 1 && floatLength != 0) {
+ Log.e(CarLog.TAG_HAL, "Wrong data type");
+ } else {
+ int value = mNativeIntData[intDataIndex];
+ service.handleIntHalEvent(property, value, timeStamp);
+ }
+ break;
+ case HalPropertyConst.VehicleValueType.VEHICLE_VALUE_TYPE_FLOAT:
+ if (intLength != 0 && floatLength != 1) {
+ Log.e(CarLog.TAG_HAL, "Wrong data type");
+ } else {
+ float value = mNativeFloatData[floatDataIndex];
+ service.handleFloatHalEvent(property, value, timeStamp);
+ }
+ break;
+ //TODO handle other types
+ default:
+ Log.e(CarLog.TAG_HAL, "onHalEvents not implemented for type " + dataType);
+ break;
+ }
+ intDataIndex += intLength;
+ floatDataIndex += floatLength;
+ }
+ }
+
+ /**
+ * Init native part with passing arrays to use for
+ * {@link #onHalDataEvents(int)}.
+ * @param props
+ * @param timestamps
+ * @param dataTypes
+ * @param intValues
+ * @param floatValues
+ * @return
+ */
+ private native long nativeInit(int[] propetyInfos, long[] timestamps, int[] intValues,
+ float[] floatValues);
+ private native void nativeRelease(long nativePtr);
+ private native HalProperty[] getSupportedProperties(long nativePtr);
+ private native void setIntProperty(long nativePtr, int property, int value);
+ private native int getIntProperty(long nativePtr, int property);
+ private native void setFloatProperty(long nativePtr, int property, float value);
+ private native float getFloatProperty(long nativePtr, int property);
+
+ /**
+ * subsribe property.
+ * @param nativePtr
+ * @param propertyHandle
+ * @param sampleRateHz
+ * @return error code, 0 for ok.
+ */
+ private native int subscribeProperty(long nativePtr, int property, float sampleRateHz);
+ private native void unsubscribeProperty(long nativePtr, int property);
+
+ private class DefaultHandler extends Handler {
+ private static final int MSG_INIT = 0;
+
+ private DefaultHandler(Looper looper) {
+ super(looper);
+ }
+
+ private void requestInit() {
+ Message msg = obtainMessage(MSG_INIT);
+ sendMessage(msg);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_INIT:
+ doInit();
+ break;
+ default:
+ Log.w(CarLog.TAG_HAL, "unown message:" + msg.what, new RuntimeException());
+ break;
+ }
+ }
+ }
+
+ public void dump(PrintWriter writer) {
+ writer.println("**dump HAL services**");
+ for (HalServiceBase service: mAllServices) {
+ service.dump(writer);
+ }
+ }
+}
diff --git a/tests/api_test/Android.mk b/tests/api_test/Android.mk
index 614bb9304b..bd5c7dc9cd 100644
--- a/tests/api_test/Android.mk
+++ b/tests/api_test/Android.mk
@@ -22,6 +22,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := CarApiTest
+#LOCAL_MODULE_TAGS := tests
+
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_STATIC_JAVA_LIBRARIES += libcarsupport
diff --git a/tests/car_activity_test_app/Android.mk b/tests/car_activity_test_app/Android.mk
index 7d0f819cbf..9ee91864c5 100644
--- a/tests/car_activity_test_app/Android.mk
+++ b/tests/car_activity_test_app/Android.mk
@@ -22,6 +22,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := CarActivityTestApp
+LOCAL_MODULE_TAGS := tests
+
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_STATIC_JAVA_LIBRARIES += libcarsupport
diff --git a/tests/car_activity_test_app/AndroidManifest.xml b/tests/car_activity_test_app/AndroidManifest.xml
index 9367b35ee6..02197e010e 100644
--- a/tests/car_activity_test_app/AndroidManifest.xml
+++ b/tests/car_activity_test_app/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
package="com.android.support.car.test.caractivitytest" >
-
+ <uses-permission android:name="android.support.car.permission.CAR_SPEED" />
<application android:label="CarActivityTest"
android:icon="@drawable/ic_launcher">
<activity android:name=".HelloCarProxyActivity"
diff --git a/tests/car_activity_test_app/res/layout/hello_caractivity.xml b/tests/car_activity_test_app/res/layout/hello_caractivity.xml
index 4f6ddd4f18..d02a7d4e60 100644
--- a/tests/car_activity_test_app/res/layout/hello_caractivity.xml
+++ b/tests/car_activity_test_app/res/layout/hello_caractivity.xml
@@ -1,20 +1,37 @@
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
-
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignLeft="@+id/textView1"
- android:layout_below="@+id/textView1"
android:layout_marginTop="24dp"
android:text="@string/button1" />
-</RelativeLayout> \ No newline at end of file
+ <TextView
+ android:id="@+id/textView_dring_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/hello_world" />
+ <TextView
+ android:id="@+id/textView_gear"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/hello_world" />
+ <TextView
+ android:id="@+id/textView_speed"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/hello_world" />
+ <TextView
+ android:id="@+id/textView_parking_brake"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/hello_world" />
+</LinearLayout>
diff --git a/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/HelloCarActivity.java b/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/HelloCarActivity.java
index 2bbf826c81..2701ba5c9e 100644
--- a/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/HelloCarActivity.java
+++ b/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/HelloCarActivity.java
@@ -21,6 +21,9 @@ import android.os.Bundle;
import android.support.car.Car;
import android.support.car.CarNotConnectedException;
import android.support.car.CarNotSupportedException;
+import android.support.car.CarSensorEvent;
+import android.support.car.CarSensorManager;
+import android.support.car.CarSensorManager.CarSensorEventListener;
import android.support.car.app.CarActivity;
import android.util.Log;
import android.view.View;
@@ -32,7 +35,32 @@ public class HelloCarActivity extends CarActivity {
private static final String TAG = HelloCarActivity.class.getSimpleName();
private TextView mTextView1;
private Button mButton1;
+ private TextView mTextViewDrivingStatus;
+ private TextView mTextViewGear;
+ private TextView mTextViewParkingBrake;
+ private TextView mTextViewSpeed;
private int mClickCount = 0;
+ private CarSensorManager mCarSensorManager;
+ private final CarSensorEventListener mListener = new CarSensorEventListener() {
+
+ @Override
+ public void onSensorChanged(CarSensorEvent event) {
+ switch (event.sensorType) {
+ case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
+ mTextViewSpeed.setText("speed:" + event.floatValues[0]);
+ break;
+ case CarSensorManager.SENSOR_TYPE_GEAR:
+ mTextViewGear.setText("gear:" + event.intValues[0]);
+ break;
+ case CarSensorManager.SENSOR_TYPE_PARKING_BRAKE:
+ mTextViewParkingBrake.setText("parking brake:" + event.intValues[0]);
+ break;
+ case CarSensorManager.SENSOR_TYPE_DRIVING_STATUS:
+ mTextViewDrivingStatus.setText("driving status:" + event.intValues[0]);
+ break;
+ }
+ }
+ };
public HelloCarActivity(Proxy proxy, Context context, Car carApi) {
super(proxy, context, carApi);
@@ -53,6 +81,19 @@ public class HelloCarActivity extends CarActivity {
mTextView1.setText("You clicked :" + mClickCount);
}
});
+ mTextViewDrivingStatus = (TextView) findViewById(R.id.textView_dring_status);
+ mTextViewGear = (TextView) findViewById(R.id.textView_gear);
+ mTextViewParkingBrake = (TextView) findViewById(R.id.textView_parking_brake);
+ mTextViewSpeed = (TextView) findViewById(R.id.textView_speed);
+ mCarSensorManager = (CarSensorManager) getCarApi().getCarManager(Car.SENSOR_SERVICE);
+ mCarSensorManager.registerListener(mListener, CarSensorManager.SENSOR_TYPE_CAR_SPEED,
+ CarSensorManager.SENSOR_RATE_NORMAL);
+ mCarSensorManager.registerListener(mListener, CarSensorManager.SENSOR_TYPE_GEAR,
+ CarSensorManager.SENSOR_RATE_NORMAL);
+ mCarSensorManager.registerListener(mListener, CarSensorManager.SENSOR_TYPE_PARKING_BRAKE,
+ CarSensorManager.SENSOR_RATE_NORMAL);
+ mCarSensorManager.registerListener(mListener, CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
+ CarSensorManager.SENSOR_RATE_NORMAL);
Log.i(TAG, "onCreate");
}
@@ -89,6 +130,7 @@ public class HelloCarActivity extends CarActivity {
@Override
protected void onDestroy() throws CarNotConnectedException {
super.onDestroy();
+ mCarSensorManager.unregisterListener(mListener);
Log.i(TAG, "onDestroy");
}