diff options
author | keunyoung <keunyoung@google.com> | 2015-08-12 10:46:27 -0700 |
---|---|---|
committer | Rakesh Iyer <rni@google.com> | 2015-11-17 15:04:55 -0800 |
commit | cc449f7941456a0133ff8a4b2e49737f0936c1d0 (patch) | |
tree | 14353f30f62be16c4a68cd0c75a182893fd0e659 | |
parent | ca515079e9fc0c35b1498830f67378e9ccf949e5 (diff) | |
download | Car-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)
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 Binary files differnew file mode 100644 index 0000000000..a8f719fd9e --- /dev/null +++ b/service/res/drawable-hdpi/car_ic_mode.png diff --git a/service/res/drawable-mdpi/car_ic_mode.png b/service/res/drawable-mdpi/car_ic_mode.png Binary files differnew file mode 100644 index 0000000000..38a97479d4 --- /dev/null +++ b/service/res/drawable-mdpi/car_ic_mode.png diff --git a/service/res/drawable-xhdpi/car_ic_mode.png b/service/res/drawable-xhdpi/car_ic_mode.png Binary files differnew file mode 100644 index 0000000000..58a1aca66f --- /dev/null +++ b/service/res/drawable-xhdpi/car_ic_mode.png diff --git a/service/res/drawable-xxhdpi/car_ic_mode.png b/service/res/drawable-xxhdpi/car_ic_mode.png Binary files differnew file mode 100644 index 0000000000..e82cdd4c95 --- /dev/null +++ b/service/res/drawable-xxhdpi/car_ic_mode.png 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"); } |