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)
diff --git a/service/src/com/android/car/BootReceiver.java b/service/src/com/android/car/BootReceiver.java
index 5bfa96c..c13a1f3 100644
--- a/service/src/com/android/car/BootReceiver.java
+++ b/service/src/com/android/car/BootReceiver.java
@@ -23,7 +23,7 @@
import android.content.Intent;
import android.util.Log;
-import com.android.car.hal.Hal;
+import com.android.car.hal.VehicleHal;
/**
@@ -34,7 +34,7 @@
@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 f8bdff9..bd6d718 100644
--- a/service/src/com/android/car/CarLog.java
+++ b/service/src/com/android/car/CarLog.java
@@ -20,4 +20,5 @@
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 0000000..35c29fa
--- /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 cd899af..d28f838 100644
--- a/service/src/com/android/car/CarSensorService.java
+++ b/service/src/com/android/car/CarSensorService.java
@@ -19,8 +19,13 @@
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.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 @@
@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 @@
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 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 @@
@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 @@
}
@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 @@
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 @@
* @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 @@
// 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 @@
}
if (sensorListeners.getRate() > rate) {
sensorListeners.setRate(rate);
- shouldStartSensors = isSensorRealLocked(sensorType);
+ shouldStartSensors = sensorSupportRate(sensorType);
}
sensorClient.addSensor(sensorType);
} finally {
@@ -325,6 +387,30 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+ 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 @@
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");
- }
- }
- } catch (RemoteException e) {
- //ignore. crash will be handled by death handler
+ 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");
+ }
+ }
+ mSensorsToDispatch.clear();
}
void release() {
if (mActive) {
mListener.asBinder().unlinkToDeath(this, 0);
mActiveSensors.clear();
+ mSensorsToDispatch.clear();
mActive = false;
}
}
@@ -745,12 +941,9 @@
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 @@
+ " 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 @@
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 32e47ff..a342e05 100644
--- a/service/src/com/android/car/CarService.java
+++ b/service/src/com/android/car/CarService.java
@@ -18,6 +18,8 @@
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 @@
@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 0000000..49cd8f0
--- /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 2a9c426..23f36d9 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.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 de91318..107b228 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.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 @@
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 @@
@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 @@
int drivingState = recalcDrivingStateLocked();
if (drivingState != mDringState && mSensorListener != null) {
mDringState = drivingState;
- mSensorListener.onSensorData(createEvent(mDringState));
+ mSensorListener.onSensorEvent(createEvent(mDringState));
}
break;
default:
@@ -184,24 +188,24 @@
}
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 081a33a..598ac8d 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.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 @@
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 ICarImpl(Context serviceContext) {
mContext = serviceContext;
- mHal = Hal.getInstance(serviceContext.getApplicationContext());
+ mHal = VehicleHal.getInstance(serviceContext.getApplicationContext());
mCarSensorService = new CarSensorService(serviceContext);
}
@@ -69,7 +71,7 @@
private void release() {
mCarSensorService.release();
- Hal.releaseInstance();
+ VehicleHal.releaseInstance();
}
@Override
@@ -125,4 +127,9 @@
//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 58e4319..0000000
--- 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 0000000..fe70246
--- /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 0000000..1616644
--- /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 0000000..680b5b8
--- /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 bc8fd5c..0000000
--- 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/SensorHalBase.java b/service/src/com/android/car/hal/SensorHalBase.java
deleted file mode 100644
index 12331b2..0000000
--- a/service/src/com/android/car/hal/SensorHalBase.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.android.car.hal;
-
-import android.support.car.CarSensorEvent;
-
-import java.io.PrintWriter;
-
-
-/**
- * Common base for all SensorHal implementation.
- * It is wholly based on subscription and there is no explicit API for polling, but each sensor
- * should report its initial state immediately after {@link #requestSensorStart(int, int)} call.
- * It is ok to report sensor data {@link SensorListener#onSensorData(CarSensorEvent)} inside
- * the {@link #requestSensorStart(int, int)} call.
- */
-public abstract class SensorHalBase {
- /**
- * 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()}.
- */
- void onSensorHalReady(SensorHalBase hal);
- /**
- * Sensor data is available.
- * @param event
- */
- void onSensorData(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);
-
- /**
- * Sensor HAL should be ready after init call.
- * @return
- */
- public abstract boolean isReady();
-
- 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/SensorHalService.java b/service/src/com/android/car/hal/SensorHalService.java
new file mode 100644
index 0000000..50edeed
--- /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/SensorHalServiceBase.java b/service/src/com/android/car/hal/SensorHalServiceBase.java
new file mode 100644
index 0000000..cf6e20a
--- /dev/null
+++ b/service/src/com/android/car/hal/SensorHalServiceBase.java
@@ -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.
+ */
+
+package com.android.car.hal;
+
+import android.support.car.CarSensorEvent;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+
+/**
+ * Common base for all SensorHal implementation.
+ * It is wholly based on subscription and there is no explicit API for polling, but each sensor
+ * should report its initial state immediately after {@link #requestSensorStart(int, int)} call.
+ * It is ok to report sensor data {@link SensorListener#onSensorData(CarSensorEvent)} inside
+ * the {@link #requestSensorStart(int, int)} call.
+ */
+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 SensorHalServiceBase#init()}.
+ */
+ void onSensorHalReady(SensorHalServiceBase hal);
+ /**
+ * Sensor events are available.
+ * @param events
+ */
+ void onSensorEvents(List<CarSensorEvent> events);
+
+ /**
+ * A sensor event is available.
+ * @param event
+ */
+ void onSensorEvent(CarSensorEvent event);
+ }
+
+ public abstract void registerSensorListener(SensorListener listener);
+
+ /**
+ * Sensor HAL should be ready after init call.
+ * @return
+ */
+ 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);
+}
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 0000000..68daf68
--- /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);
+ }
+ }
+}