diff options
-rw-r--r-- | api/system-current.txt | 3 | ||||
-rw-r--r-- | core/java/android/hardware/Sensor.java | 18 | ||||
-rw-r--r-- | core/java/android/hardware/SensorManager.java | 100 | ||||
-rw-r--r-- | core/java/android/hardware/SystemSensorManager.java | 112 | ||||
-rw-r--r-- | core/jni/android_hardware_SensorManager.cpp | 33 |
5 files changed, 254 insertions, 12 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index fed8849..87e130c 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -13256,6 +13256,7 @@ package android.hardware { method public int getType(); method public java.lang.String getVendor(); method public int getVersion(); + method public boolean isDataInjectionSupported(); method public boolean isWakeUpSensor(); field public static final int REPORTING_MODE_CONTINUOUS = 0; // 0x0 field public static final int REPORTING_MODE_ONE_SHOT = 2; // 0x2 @@ -13331,6 +13332,7 @@ package android.hardware { public abstract class SensorManager { method public boolean cancelTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor); + method public boolean enableDataInjectionMode(boolean); method public boolean flush(android.hardware.SensorEventListener); method public static float getAltitude(float, float); method public static void getAngleChange(float[], float[], float[]); @@ -13343,6 +13345,7 @@ package android.hardware { method public static void getRotationMatrixFromVector(float[], float[]); method public java.util.List<android.hardware.Sensor> getSensorList(int); method public deprecated int getSensors(); + method public boolean injectSensorData(android.hardware.Sensor, float[], int, long); method public deprecated boolean registerListener(android.hardware.SensorListener, int); method public deprecated boolean registerListener(android.hardware.SensorListener, int, int); method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int); diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index 39f4cca..e875864 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -580,6 +580,10 @@ public final class Sensor { private static final int REPORTING_MODE_MASK = 0xE; private static final int REPORTING_MODE_SHIFT = 1; + // MASK for LSB fifth bit. Used to know whether the sensor supports data injection or not. + private static final int DATA_INJECTION_MASK = 0x10; + private static final int DATA_INJECTION_SHIFT = 4; + // TODO(): The following arrays are fragile and error-prone. This needs to be refactored. // Note: This needs to be updated, whenever a new sensor is added. @@ -822,6 +826,20 @@ public final class Sensor { return (mFlags & SENSOR_FLAG_WAKE_UP_SENSOR) != 0; } + /** + * Returns true if the sensor supports data injection when the + * HAL is set to data injection mode. + * + * @return <code>true</code> if the sensor supports data + * injection when the HAL is set in injection mode, + * false otherwise. + * @hide + */ + @SystemApi + public boolean isDataInjectionSupported() { + return (((mFlags & DATA_INJECTION_MASK) >> DATA_INJECTION_SHIFT)) != 0; + } + void setRange(float max, float res) { mMaxRange = max; mResolution = res; diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index 34b895b..861969e 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -16,7 +16,10 @@ package android.hardware; +import android.annotation.SystemApi; +import android.os.Build; import android.os.Handler; +import android.os.SystemClock; import android.util.Log; import android.util.SparseArray; @@ -1555,6 +1558,103 @@ public abstract class SensorManager { Sensor sensor, boolean disable); + /** + * For testing purposes only. Not for third party applications. + * + * Enable data injection mode in sensor service. This mode is + * expected to be used only for testing purposes. If the HAL is + * set to data injection mode, it will ignore the input from + * physical sensors and read sensor data that is injected from + * the test application. This mode is used for testing vendor + * implementations for various algorithms like Rotation Vector, + * Significant Motion, Step Counter etc. + * + * The tests which call this API need to have {@code + * android.permission.HARDWARE_TEST} permission which isn't + * available for third party applications. + * + * @param enable True to set the HAL in DATA_INJECTION mode. + * False to reset the HAL back to NORMAL mode. + * + * @return true if the HAL supports data injection and false + * otherwise. + * @hide + */ + @SystemApi + public boolean enableDataInjectionMode(boolean enable) { + return enableDataInjectionImpl(enable); + } + + /** + * @hide + */ + protected abstract boolean enableDataInjectionImpl(boolean enable); + + /** + * For testing purposes only. Not for third party applications. + * + * This method is used to inject raw sensor data into the HAL. + * Call enableDataInjection before this method to set the HAL in + * data injection mode. This method should be called only if a + * previous call to enableDataInjection has been successful and + * the HAL is already in data injection mode. + * + * The tests which call this API need to have {@code + * android.permission.HARDWARE_TEST} permission which isn't + * available for third party applications. + * + * @param sensor The sensor to inject. + * @param values Sensor values to inject. The length of this + * array must be exactly equal to the number of + * values reported by the sensor type. + * @param accuracy Accuracy of the sensor. + * @param timestamp Sensor timestamp associated with the event. + * + * @return boolean True if the data injection succeeds, false + * otherwise. + * @throws IllegalArgumentException when the sensor is null, + * data injection is not supported by the sensor, values + * are null, incorrect number of values for the sensor, + * sensor accuracy is incorrect or timestamps are + * invalid. + * @hide + */ + @SystemApi + public boolean injectSensorData(Sensor sensor, float[] values, int accuracy, + long timestamp) { + if (sensor == null) { + throw new IllegalArgumentException("sensor cannot be null"); + } + if (!sensor.isDataInjectionSupported()) { + throw new IllegalArgumentException("sensor does not support data injection"); + } + if (values == null) { + throw new IllegalArgumentException("sensor data cannot be null"); + } + int expectedNumValues = Sensor.getMaxLengthValuesArray(sensor, Build.VERSION_CODES.MNC); + if (values.length != expectedNumValues) { + throw new IllegalArgumentException ("Wrong number of values for sensor " + + sensor.getName() + " actual=" + values.length + " expected=" + + expectedNumValues); + } + if (accuracy < SENSOR_STATUS_NO_CONTACT || accuracy > SENSOR_STATUS_ACCURACY_HIGH) { + throw new IllegalArgumentException("Invalid sensor accuracy"); + } + if (timestamp <= 0) { + throw new IllegalArgumentException("Negative or zero sensor timestamp"); + } + if (timestamp > SystemClock.elapsedRealtimeNanos()) { + throw new IllegalArgumentException("Sensor timestamp into the future"); + } + return injectSensorDataImpl(sensor, values, accuracy, timestamp); + } + + /** + * @hide + */ + protected abstract boolean injectSensorDataImpl(Sensor sensor, float[] values, int accuracy, + long timestamp); + private LegacySensorManager getLegacySensorManager() { synchronized (mSensorListByType) { if (mLegacySensorManager == null) { diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 7ad3a68..11037fd 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -16,7 +16,9 @@ package android.hardware; +import android.Manifest; import android.content.Context; +import android.content.pm.PackageManager; import android.os.Handler; import android.os.Looper; import android.os.MessageQueue; @@ -40,11 +42,14 @@ import java.util.List; public class SystemSensorManager extends SensorManager { private static native void nativeClassInit(); private static native int nativeGetNextSensor(Sensor sensor, int next); + private static native int nativeEnableDataInjection(boolean enable); private static boolean sSensorModuleInitialized = false; private static final Object sSensorModuleLock = new Object(); private static final ArrayList<Sensor> sFullSensorsList = new ArrayList<Sensor>(); private static final SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>(); + private static InjectEventQueue mInjectEventQueue = null; + private static boolean mDataInjectionMode = false; // Listener list private final HashMap<SensorEventListener, SensorEventQueue> mSensorListeners = @@ -56,6 +61,7 @@ public class SystemSensorManager extends SensorManager { private final Looper mMainLooper; private final int mTargetSdkLevel; private final String mPackageName; + private final boolean mHasDataInjectionPermissions; /** {@hide} */ public SystemSensorManager(Context context, Looper mainLooper) { @@ -82,6 +88,8 @@ public class SystemSensorManager extends SensorManager { } } while (i>0); } + mHasDataInjectionPermissions = context.checkSelfPermission( + Manifest.permission.HARDWARE_TEST) == PackageManager.PERMISSION_GRANTED; } } @@ -219,19 +227,72 @@ public class SystemSensorManager extends SensorManager { } } + protected boolean enableDataInjectionImpl(boolean enable) { + if (!mHasDataInjectionPermissions) { + throw new SecurityException("Permission denial. Calling enableDataInjection without " + + Manifest.permission.HARDWARE_TEST); + } + synchronized (sSensorModuleLock) { + int ret = nativeEnableDataInjection(enable); + // The HAL does not support injection. Ignore. + if (ret != 0) { + Log.e(TAG, "HAL does not support data injection"); + return false; + } + mDataInjectionMode = enable; + // If data injection is being disabled clean up the native resources. + if (!enable && mInjectEventQueue != null) { + mInjectEventQueue.dispose(); + mInjectEventQueue = null; + } + return true; + } + } + + protected boolean injectSensorDataImpl(Sensor sensor, float[] values, int accuracy, + long timestamp) { + if (!mHasDataInjectionPermissions) { + throw new SecurityException("Permission denial. Calling injectSensorData without " + + Manifest.permission.HARDWARE_TEST); + } + synchronized (sSensorModuleLock) { + if (!mDataInjectionMode) { + Log.e(TAG, "Data injection mode not activated before calling injectSensorData"); + return false; + } + if (mInjectEventQueue == null) { + mInjectEventQueue = new InjectEventQueue(mMainLooper, this); + } + int ret = mInjectEventQueue.injectSensorData(sensor.getHandle(), values, accuracy, + timestamp); + // If there are any errors in data injection clean up the native resources. + if (ret != 0) { + mInjectEventQueue.dispose(); + mInjectEventQueue = null; + mDataInjectionMode = false; + } + return ret == 0; + } + } + /* * BaseEventQueue is the communication channel with the sensor service, * SensorEventQueue, TriggerEventQueue are subclases and there is one-to-one mapping between - * the queues and the listeners. + * the queues and the listeners. InjectEventQueue is also a sub-class which is a special case + * where data is being injected into the sensor HAL through the sensor service. It is not + * associated with any listener and there is one InjectEventQueue associated with a + * SensorManager instance. */ private static abstract class BaseEventQueue { private native long nativeInitBaseEventQueue(WeakReference<BaseEventQueue> eventQWeak, - MessageQueue msgQ, float[] scratch, String packageName); + MessageQueue msgQ, float[] scratch, String packageName, int mode); private static native int nativeEnableSensor(long eventQ, int handle, int rateUs, int maxBatchReportLatencyUs); private static native int nativeDisableSensor(long eventQ, int handle); private static native void nativeDestroySensorEventQueue(long eventQ); private static native int nativeFlushSensor(long eventQ); + private static native int nativeInjectSensorData(long eventQ, int handle, + float[] values,int accuracy, long timestamp); private long nSensorEventQueue; private final SparseBooleanArray mActiveSensors = new SparseBooleanArray(); protected final SparseIntArray mSensorAccuracies = new SparseIntArray(); @@ -240,10 +301,12 @@ public class SystemSensorManager extends SensorManager { private final float[] mScratch = new float[16]; protected final SystemSensorManager mManager; - BaseEventQueue(Looper looper, SystemSensorManager manager) { + protected static final int OPERATING_MODE_NORMAL = 0; + protected static final int OPERATING_MODE_DATA_INJECTION = 1; + + BaseEventQueue(Looper looper, SystemSensorManager manager, int mode) { nSensorEventQueue = nativeInitBaseEventQueue(new WeakReference<BaseEventQueue>(this), - looper.getQueue(), mScratch, - manager.mPackageName); + looper.getQueue(), mScratch, manager.mPackageName, mode); mCloseGuard.open("dispose"); mManager = manager; } @@ -340,6 +403,11 @@ public class SystemSensorManager extends SensorManager { maxBatchReportLatencyUs); } + protected int injectSensorDataBase(int handle, float[] values, int accuracy, + long timestamp) { + return nativeInjectSensorData(nSensorEventQueue, handle, values, accuracy, timestamp); + } + private int disableSensor(Sensor sensor) { if (nSensorEventQueue == 0) throw new NullPointerException(); if (sensor == null) throw new NullPointerException(); @@ -359,7 +427,7 @@ public class SystemSensorManager extends SensorManager { public SensorEventQueue(SensorEventListener listener, Looper looper, SystemSensorManager manager) { - super(looper, manager); + super(looper, manager, OPERATING_MODE_NORMAL); mListener = listener; } @@ -426,7 +494,7 @@ public class SystemSensorManager extends SensorManager { public TriggerEventQueue(TriggerEventListener listener, Looper looper, SystemSensorManager manager) { - super(looper, manager); + super(looper, manager, OPERATING_MODE_NORMAL); mListener = listener; } @@ -477,4 +545,34 @@ public class SystemSensorManager extends SensorManager { protected void dispatchFlushCompleteEvent(int handle) { } } + + static final class InjectEventQueue extends BaseEventQueue { + public InjectEventQueue(Looper looper, SystemSensorManager manager) { + super(looper, manager, OPERATING_MODE_DATA_INJECTION); + } + + int injectSensorData(int handle, float[] values,int accuracy, long timestamp) { + return injectSensorDataBase(handle, values, accuracy, timestamp); + } + + @SuppressWarnings("unused") + protected void dispatchSensorEvent(int handle, float[] values, int accuracy, + long timestamp) { + } + + @SuppressWarnings("unused") + protected void dispatchFlushCompleteEvent(int handle) { + + } + + @SuppressWarnings("unused") + protected void addSensorEvent(Sensor sensor) { + + } + + @SuppressWarnings("unused") + protected void removeSensorEvent(Sensor sensor) { + + } + } } diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index 16e5b3c..0cf596c 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -174,6 +174,11 @@ nativeGetNextSensor(JNIEnv *env, jclass clazz, jobject sensor, jint next) return size_t(next) < count ? next : 0; } +static int nativeEnableDataInjection(JNIEnv *_env, jclass _this, jboolean enable) { + SensorManager& mgr(SensorManager::getInstance()); + return mgr.enableDataInjection(enable); +} + //---------------------------------------------------------------------------- class Receiver : public LooperCallback { @@ -277,11 +282,11 @@ private: }; static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jobject eventQWeak, jobject msgQ, - jfloatArray scratch, jstring packageName) { + jfloatArray scratch, jstring packageName, jint mode) { SensorManager& mgr(SensorManager::getInstance()); ScopedUtfChars packageUtf(env, packageName); String8 clientName(packageUtf.c_str()); - sp<SensorEventQueue> queue(mgr.createEventQueue(clientName)); + sp<SensorEventQueue> queue(mgr.createEventQueue(clientName, mode)); sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, msgQ); if (messageQueue == NULL) { @@ -297,7 +302,6 @@ static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jobject event static jint nativeEnableSensor(JNIEnv *env, jclass clazz, jlong eventQ, jint handle, jint rate_us, jint maxBatchReportLatency) { sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ)); - return receiver->getSensorEventQueue()->enableSensor(handle, rate_us, maxBatchReportLatency, 0); } @@ -307,7 +311,7 @@ static jint nativeDisableSensor(JNIEnv *env, jclass clazz, jlong eventQ, jint ha return receiver->getSensorEventQueue()->disableSensor(handle); } -static void nativeDestroySensorEventQueue(JNIEnv *env, jclass clazz, jlong eventQ, jint handle) { +static void nativeDestroySensorEventQueue(JNIEnv *env, jclass clazz, jlong eventQ) { sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ)); receiver->destroy(); receiver->decStrong((void*)nativeInitSensorEventQueue); @@ -318,6 +322,17 @@ static jint nativeFlushSensor(JNIEnv *env, jclass clazz, jlong eventQ) { return receiver->getSensorEventQueue()->flush(); } +static jint nativeInjectSensorData(JNIEnv *env, jclass clazz, jlong eventQ, jint handle, + jfloatArray values, jint accuracy, jlong timestamp) { + sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ)); + // Create a sensor_event from the above data which can be injected into the HAL. + ASensorEvent sensor_event; + memset(&sensor_event, 0, sizeof(sensor_event)); + sensor_event.sensor = handle; + sensor_event.timestamp = timestamp; + env->GetFloatArrayRegion(values, 0, env->GetArrayLength(values), sensor_event.data); + return receiver->getSensorEventQueue()->injectSensorEvent(sensor_event); +} //---------------------------------------------------------------------------- static JNINativeMethod gSystemSensorManagerMethods[] = { @@ -328,11 +343,15 @@ static JNINativeMethod gSystemSensorManagerMethods[] = { {"nativeGetNextSensor", "(Landroid/hardware/Sensor;I)I", (void*)nativeGetNextSensor }, + + {"nativeEnableDataInjection", + "(Z)I", + (void*)nativeEnableDataInjection }, }; static JNINativeMethod gBaseEventQueueMethods[] = { {"nativeInitBaseEventQueue", - "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;[FLjava/lang/String;)J", + "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;[FLjava/lang/String;I)J", (void*)nativeInitSensorEventQueue }, {"nativeEnableSensor", @@ -350,6 +369,10 @@ static JNINativeMethod gBaseEventQueueMethods[] = { {"nativeFlushSensor", "(J)I", (void*)nativeFlushSensor }, + + {"nativeInjectSensorData", + "(JI[FIJ)I", + (void*)nativeInjectSensorData }, }; }; // namespace android |