Add new sensor types.
1. Add uncalibrated gyros and magnetic field sensor.
2. Change max number of events from 3 to 16.
3. Add new APIs for trigger sensors.
Change-Id: I1957d723de2b65c31dadaee7386fd8d51ea2f7e5
diff --git a/api/current.txt b/api/current.txt
index 2e1dcf6..b173c70 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10044,16 +10044,20 @@
field public static final int TYPE_ACCELEROMETER = 1; // 0x1
field public static final int TYPE_ALL = -1; // 0xffffffff
field public static final int TYPE_AMBIENT_TEMPERATURE = 13; // 0xd
+ field public static final int TYPE_GAME_ROTATION_VECTOR = 15; // 0xf
field public static final int TYPE_GRAVITY = 9; // 0x9
field public static final int TYPE_GYROSCOPE = 4; // 0x4
+ field public static final int TYPE_GYROSCOPE_UNCALIBRATED = 16; // 0x10
field public static final int TYPE_LIGHT = 5; // 0x5
field public static final int TYPE_LINEAR_ACCELERATION = 10; // 0xa
field public static final int TYPE_MAGNETIC_FIELD = 2; // 0x2
+ field public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; // 0xe
field public static final deprecated int TYPE_ORIENTATION = 3; // 0x3
field public static final int TYPE_PRESSURE = 6; // 0x6
field public static final int TYPE_PROXIMITY = 8; // 0x8
field public static final int TYPE_RELATIVE_HUMIDITY = 12; // 0xc
field public static final int TYPE_ROTATION_VECTOR = 11; // 0xb
+ field public static final int TYPE_SIGNIFICANT_MOTION = 17; // 0x11
field public static final deprecated int TYPE_TEMPERATURE = 7; // 0x7
}
@@ -10075,6 +10079,7 @@
}
public abstract class SensorManager {
+ method public boolean cancelTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
method public static float getAltitude(float, float);
method public static void getAngleChange(float[], float[], float[]);
method public android.hardware.Sensor getDefaultSensor(int);
@@ -10090,6 +10095,7 @@
method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int);
method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, android.os.Handler);
method public static boolean remapCoordinateSystem(float[], int, int, float[]);
+ method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
method public deprecated void unregisterListener(android.hardware.SensorListener);
method public deprecated void unregisterListener(android.hardware.SensorListener, int);
method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor);
@@ -10153,6 +10159,17 @@
field public static final float STANDARD_GRAVITY = 9.80665f;
}
+ public final class TriggerEvent {
+ field public android.hardware.Sensor sensor;
+ field public long timestamp;
+ field public final float[] values;
+ }
+
+ public abstract class TriggerEventListener {
+ ctor public TriggerEventListener();
+ method public abstract void onTrigger(android.hardware.TriggerEvent);
+ }
+
}
package android.hardware.display {
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 41384d2..af4c074 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -114,11 +114,90 @@
/** A constant describing an ambient temperature sensor type */
public static final int TYPE_AMBIENT_TEMPERATURE = 13;
- /**
+ /**
+ * A constant describing a magnetic field uncalibrated sensor type. See
+ * {@link android.hardware.SensorEvent#values SensorEvent.values} for more
+ * details.
+ * <p>
+ * No periodic calibration is performed (ie: there are no discontinuities
+ * in the data stream while using this sensor). Assumptions that the
+ * magnetic field is due to the Earth's poles is avoided. Factory calibration
+ * and temperature compensation is still performed.
+ * </p>
+ */
+ public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14;
+
+ /**
+ * Identical to {@link #TYPE_ROTATION_VECTOR} except that it doesn't
+ * use the geomagnetic field. Therefore the Y axis doesn't
+ * point north, but instead to some other reference, that reference is
+ * allowed to drift by the same order of magnitude as the gyroscope
+ * drift around the Z axis.
+ * <p>
+ * In the ideal case, a phone rotated and returning to the same real-world
+ * orientation should report the same game rotation vector
+ * (without using the earth's geomagnetic field). However, the orientation
+ * may drift somewhat over time.
+ * </p>
+ */
+
+ public static final int TYPE_GAME_ROTATION_VECTOR = 15;
+
+ /**
+ * A constant describing a gyroscope uncalibrated sensor type. See
+ * {@link android.hardware.SensorEvent#values SensorEvent.values} for more
+ * details.
+ * <p>
+ * No gyro-drift compensation is performed.
+ * Factory calibration and temperature compensation is still applied
+ * to the rate of rotation (angular speeds).
+ * </p>
+ */
+ public static final int TYPE_GYROSCOPE_UNCALIBRATED = 16;
+
+ /**
+ * A constant describing the significant motion trigger sensor.
+ * See {@link android.hardware.SensorEvent#values} for more details.
+ * <p>
+ * It triggers when an event occurs and then automatically disables
+ * itself. The sensor continues to operate while the device is asleep
+ * and will automatically wake the device to notify when significant
+ * motion is detected. The application does not need to hold any wake
+ * locks for this sensor to trigger.
+ * </p>
+ */
+ public static final int TYPE_SIGNIFICANT_MOTION = 17;
+
+ /**
* A constant describing all sensor types.
*/
public static final int TYPE_ALL = -1;
+ /* Reporting mode constants for sensors. Each sensor will have exactly one
+ reporting mode associated with it. */
+ // Events are reported at a constant rate.
+ static int REPORTING_MODE_CONTINUOUS = 1;
+
+ // Events are reported only when the value changes.
+ static int REPORTING_MODE_ON_CHANGE = 2;
+
+ // Upon detection of an event, the sensor deactivates itself and then sends a single event.
+ static int REPORTING_MODE_ONE_SHOT = 3;
+
+ // Note: This needs to be updated, whenever a new sensor is added.
+ private static int[] sSensorReportingModes = {
+ REPORTING_MODE_CONTINUOUS, REPORTING_MODE_CONTINUOUS, REPORTING_MODE_CONTINUOUS,
+ REPORTING_MODE_CONTINUOUS, REPORTING_MODE_ON_CHANGE, REPORTING_MODE_CONTINUOUS,
+ REPORTING_MODE_ON_CHANGE, REPORTING_MODE_ON_CHANGE, REPORTING_MODE_CONTINUOUS,
+ REPORTING_MODE_CONTINUOUS, REPORTING_MODE_CONTINUOUS, REPORTING_MODE_ON_CHANGE,
+ REPORTING_MODE_ON_CHANGE, REPORTING_MODE_CONTINUOUS, REPORTING_MODE_CONTINUOUS,
+ REPORTING_MODE_CONTINUOUS, REPORTING_MODE_ONE_SHOT };
+
+ static int getReportingMode(Sensor sensor) {
+ // mType starts from offset 1.
+ return sSensorReportingModes[sensor.mType - 1];
+ }
+
/* Some of these fields are set only by the native bindings in
* SensorManager.
*/
@@ -132,7 +211,6 @@
private float mPower;
private int mMinDelay;
-
Sensor() {
}
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 51a17c1..84c9131 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -17,11 +17,9 @@
package android.hardware;
/**
- * <p>
* This class represents a {@link android.hardware.Sensor Sensor} event and
* holds informations such as the sensor's type, the time-stamp, accuracy and of
* course the sensor's {@link SensorEvent#values data}.
- * </p>
*
* <p>
* <u>Definition of the coordinate system used by the SensorEvent API.</u>
@@ -67,15 +65,9 @@
* Sensor.TYPE_ACCELEROMETER}:</h4> All values are in SI units (m/s^2)
*
* <ul>
- * <p>
- * values[0]: Acceleration minus Gx on the x-axis
- * </p>
- * <p>
- * values[1]: Acceleration minus Gy on the y-axis
- * </p>
- * <p>
- * values[2]: Acceleration minus Gz on the z-axis
- * </p>
+ * <li> values[0]: Acceleration minus Gx on the x-axis </li>
+ * <li> values[1]: Acceleration minus Gy on the y-axis </li>
+ * <li> values[2]: Acceleration minus Gz on the z-axis </li>
* </ul>
*
* <p>
@@ -165,15 +157,9 @@
* definition of positive rotation and does not agree with the definition of
* roll given earlier.
* <ul>
- * <p>
- * values[0]: Angular speed around the x-axis
- * </p>
- * <p>
- * values[1]: Angular speed around the y-axis
- * </p>
- * <p>
- * values[2]: Angular speed around the z-axis
- * </p>
+ * <li> values[0]: Angular speed around the x-axis </li>
+ * <li> values[1]: Angular speed around the y-axis </li>
+ * <li> values[2]: Angular speed around the z-axis </li>
* </ul>
* <p>
* Typically the output of the gyroscope is integrated over time to
@@ -233,22 +219,19 @@
* </p>
* <h4>{@link android.hardware.Sensor#TYPE_LIGHT Sensor.TYPE_LIGHT}:</h4>
* <ul>
- * <p>
- * values[0]: Ambient light level in SI lux units
+ * <li>values[0]: Ambient light level in SI lux units </li>
* </ul>
*
* <h4>{@link android.hardware.Sensor#TYPE_PRESSURE Sensor.TYPE_PRESSURE}:</h4>
* <ul>
- * <p>
- * values[0]: Atmospheric pressure in hPa (millibar)
+ * <li>values[0]: Atmospheric pressure in hPa (millibar) </li>
* </ul>
*
* <h4>{@link android.hardware.Sensor#TYPE_PROXIMITY Sensor.TYPE_PROXIMITY}:
* </h4>
*
* <ul>
- * <p>
- * values[0]: Proximity sensor distance measured in centimeters
+ * <li>values[0]: Proximity sensor distance measured in centimeters </li>
* </ul>
*
* <p>
@@ -304,39 +287,23 @@
* </p>
*
* <ul>
- * <p>
- * values[0]: x*sin(θ/2)
- * </p>
- * <p>
- * values[1]: y*sin(θ/2)
- * </p>
- * <p>
- * values[2]: z*sin(θ/2)
- * </p>
- * <p>
- * values[3]: cos(θ/2) <i>(optional: only if value.length = 4)</i>
- * </p>
+ * <li> values[0]: x*sin(θ/2) </li>
+ * <li> values[1]: y*sin(θ/2) </li>
+ * <li> values[2]: z*sin(θ/2) </li>
+ * <li> values[3]: cos(θ/2) <i>(optional: only if value.length = 4)</i> </li>
* </ul>
*
* <h4>{@link android.hardware.Sensor#TYPE_ORIENTATION
* Sensor.TYPE_ORIENTATION}:</h4> All values are angles in degrees.
*
* <ul>
- * <p>
- * values[0]: Azimuth, angle between the magnetic north direction and the
+ * <li> values[0]: Azimuth, angle between the magnetic north direction and the
* y-axis, around the z-axis (0 to 359). 0=North, 90=East, 180=South,
- * 270=West
- * </p>
- *
- * <p>
- * values[1]: Pitch, rotation around x-axis (-180 to 180), with positive
- * values when the z-axis moves <b>toward</b> the y-axis.
- * </p>
- *
- * <p>
- * values[2]: Roll, rotation around y-axis (-90 to 90), with positive values
- * when the x-axis moves <b>toward</b> the z-axis.
- * </p>
+ * 270=West </li>
+ * <li> values[1]: Pitch, rotation around x-axis (-180 to 180), with positive
+ * values when the z-axis moves <b>toward</b> the y-axis. </li>
+ * <li> values[2]: Roll, rotation around y-axis (-90 to 90), with positive values
+ * when the x-axis moves <b>toward</b> the z-axis. </li>
* </ul>
*
* <p>
@@ -364,9 +331,7 @@
* <h4>{@link android.hardware.Sensor#TYPE_RELATIVE_HUMIDITY
* Sensor.TYPE_RELATIVE_HUMIDITY}:</h4>
* <ul>
- * <p>
- * values[0]: Relative ambient air humidity in percent
- * </p>
+ * <li> values[0]: Relative ambient air humidity in percent </li>
* </ul>
* <p>
* When relative ambient air humidity and ambient temperature are
@@ -423,21 +388,58 @@
* </h4>
*
* <ul>
- * <p>
- * values[0]: ambient (room) temperature in degree Celsius.
+ * <li> values[0]: ambient (room) temperature in degree Celsius.</li>
* </ul>
*
* @see SensorEvent
* @see GeomagneticField
+ *
+ * <h4>{@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD_UNCALIBRATED} </h4>
+ * All values are in micro-Tesla (uT) and measure the ambient magnetic field
+ * in the X, Y and Z axis.
+ * <p>
+ * No periodic calibration is performed (ie: there are no discontinuities
+ * in the data stream while using this sensor). Assumptions that the the
+ * magnetic field is due to the Earth's poles is avoided. Factory calibration
+ * and temperature compensation is still performed.
+ * </p>
+ *
+ * <h4> {@link android.hardware.Sensor#TYPE_GYROSCOPE_UNCALIBRATED} </h4>
+ * All values are in radians/second and measure the rate of rotation
+ * around the X, Y and Z axis. An estimation of the drift on each axis is
+ * reported as well.
+ * <p>
+ * No gyro-drift compensation is performed. Factory calibration and temperature
+ * compensation is still applied to the rate of rotation (angular speeds).
+ * </p>
+ * <p>
+ * The coordinate system is the same as is used for the
+ * {@link android.hardware.Sensor#TYPE_ACCELEROMETER}
+ * Rotation is positive in the counter-clockwise direction (right-hand rule).
+ * That is, an observer looking from some positive location on the x, y or z axis
+ * at a device positioned on the origin would report positive rotation if the device
+ * appeared to be rotating counter clockwise.
+ * The range would at least be 17.45 rad/s (ie: ~1000 deg/s).
+ * <ul>
+ * <li> values[0] : angular speed (w/o drift compensation) around the X axis in rad/s </li>
+ * <li> values[1] : angular speed (w/o drift compensation) around the Y axis in rad/s </li>
+ * <li> values[2] : angular speed (w/o drift compensation) around the Z axis in rad/s </li>
+ * <li> values[3] : estimated drift around X axis in rad/s </li>
+ * <li> values[4] : estimated drift around Y axis in rad/s </li>
+ * <li> values[5] : estimated drift around Z axis in rad/s </li>
+ * </ul>
+ * </p>
+ * <h4></h4>
+ * <h4> Pro Tip: Always use the length of the values array while performing operations
+ * on it. In earlier versions, this used to be always 3 which has changed now. </h4>
*/
-
public final float[] values;
/**
* The sensor that generated this event. See
* {@link android.hardware.SensorManager SensorManager} for details.
*/
- public Sensor sensor;
+ public Sensor sensor;
/**
* The accuracy of this event. See {@link android.hardware.SensorManager
@@ -445,13 +447,11 @@
*/
public int accuracy;
-
/**
* The time in nanosecond at which the event happened
*/
public long timestamp;
-
SensorEvent(int size) {
values = new float[size];
}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index c0d2fae..ce7bc7e 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -38,7 +38,11 @@
* hours. Note that the system will <i>not</i> disable sensors automatically when
* the screen turns off.
* </p>
- *
+ * <p class="note">
+ * Note: Don't use this mechanism with a Trigger Sensor, have a look
+ * at {@link TriggerEventListener}. {@link Sensor#TYPE_SIGNIFICANT_MOTION}
+ * is an example of a trigger sensor.
+ * </p>
* <pre class="prettyprint">
* public class SensorActivity extends Activity, implements SensorEventListener {
* private final SensorManager mSensorManager;
@@ -515,6 +519,12 @@
/**
* Unregisters a listener for the sensors with which it is registered.
*
+ * <p class="note"></p>
+ * Note: Don't use this method with a one shot trigger sensor such as
+ * {@link Sensor#TYPE_SIGNIFICANT_MOTION}.
+ * Use {@link #cancelTriggerSensor(TriggerEventListener, Sensor)} instead.
+ * </p>
+ *
* @param listener
* a SensorEventListener object
*
@@ -524,6 +534,7 @@
* @see #unregisterListener(SensorEventListener)
* @see #registerListener(SensorEventListener, Sensor, int)
*
+ * @throws IllegalArgumentException when sensor is a trigger sensor.
*/
public void unregisterListener(SensorEventListener listener, Sensor sensor) {
if (listener == null || sensor == null) {
@@ -558,6 +569,12 @@
* Registers a {@link android.hardware.SensorEventListener
* SensorEventListener} for the given sensor.
*
+ * <p class="note"></p>
+ * Note: Don't use this method with a one shot trigger sensor such as
+ * {@link Sensor#TYPE_SIGNIFICANT_MOTION}.
+ * Use {@link #requestTriggerSensor(TriggerEventListener, Sensor)} instead.
+ * </p>
+ *
* @param listener
* A {@link android.hardware.SensorEventListener SensorEventListener}
* object.
@@ -581,6 +598,7 @@
* @see #unregisterListener(SensorEventListener)
* @see #unregisterListener(SensorEventListener, Sensor)
*
+ * @throws IllegalArgumentException when sensor is null or a trigger sensor
*/
public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate) {
return registerListener(listener, sensor, rate, null);
@@ -590,6 +608,12 @@
* Registers a {@link android.hardware.SensorEventListener
* SensorEventListener} for the given sensor.
*
+ * <p class="note"></p>
+ * Note: Don't use this method with a one shot trigger sensor such as
+ * {@link Sensor#TYPE_SIGNIFICANT_MOTION}.
+ * Use {@link #requestTriggerSensor(TriggerEventListener, Sensor)} instead.
+ * </p>
+ *
* @param listener
* A {@link android.hardware.SensorEventListener SensorEventListener}
* object.
@@ -617,6 +641,7 @@
* @see #unregisterListener(SensorEventListener)
* @see #unregisterListener(SensorEventListener, Sensor)
*
+ * @throws IllegalArgumentException when sensor is null or a trigger sensor
*/
public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate,
Handler handler) {
@@ -1304,6 +1329,68 @@
Q[3] = rv[2];
}
+ /**
+ * Requests receiving trigger events for a trigger sensor.
+ *
+ * <p>
+ * When the sensor detects a trigger event condition, such as significant motion in
+ * the case of the {@link Sensor#TYPE_SIGNIFICANT_MOTION}, the provided trigger listener
+ * will be invoked once and then its request to receive trigger events will be canceled.
+ * To continue receiving trigger events, the application must request to receive trigger
+ * events again.
+ * </p>
+ *
+ * @param listener The listener on which the
+ * {@link TriggerEventListener#onTrigger(TriggerEvent)} will be delivered.
+ * @param sensor The sensor to be enabled.
+ *
+ * @return true if the sensor was successfully enabled.
+ *
+ * @throws IllegalArgumentException when sensor is null or not a trigger sensor.
+ */
+ public boolean requestTriggerSensor(TriggerEventListener listener, Sensor sensor) {
+ return requestTriggerSensorImpl(listener, sensor);
+ }
+
+ /**
+ * @hide
+ */
+ protected abstract boolean requestTriggerSensorImpl(TriggerEventListener listener,
+ Sensor sensor);
+
+ /**
+ * Cancels receiving trigger events for a trigger sensor.
+ *
+ * <p>
+ * Note that a Trigger sensor will be auto disabled if
+ * {@link TriggerEventListener#onTrigger(TriggerEvent)} has triggered.
+ * This method is provided in case the user wants to explicitly cancel the request
+ * to receive trigger events.
+ * </p>
+ *
+ * @param listener The listener on which the
+ * {@link TriggerEventListener#onTrigger(TriggerEvent)}
+ * is delivered.It should be the same as the one used
+ * in {@link #requestTriggerSensor(TriggerEventListener, Sensor)}
+ * @param sensor The sensor for which the trigger request should be canceled.
+ * If null, it cancels receiving trigger for all sensors associated
+ * with the listener.
+ *
+ * @return true if successfully canceled.
+ *
+ * @throws IllegalArgumentException when sensor is a trigger sensor.
+ */
+ public boolean cancelTriggerSensor(TriggerEventListener listener, Sensor sensor) {
+ return cancelTriggerSensorImpl(listener, sensor);
+ }
+
+ /**
+ * @hide
+ */
+ protected abstract boolean cancelTriggerSensorImpl(TriggerEventListener listener,
+ Sensor sensor);
+
+
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 9591631..3c231e5 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -16,18 +16,19 @@
package android.hardware;
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.List;
-
-import dalvik.system.CloseGuard;
-
import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;
+import android.util.Log;
+import android.util.Pools;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import dalvik.system.CloseGuard;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
/**
* Sensor manager implementation that communicates with the built-in
@@ -45,22 +46,21 @@
private static final SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>();
// Listener list
- private final ArrayList<SensorEventListenerSensorPair> mListenerDelegates = new ArrayList<SensorEventListenerSensorPair>();
+ private final HashMap<SensorEventListener, SensorEventQueue> mSensorListeners =
+ new HashMap<SensorEventListener, SensorEventQueue>();
+ private final HashMap<TriggerEventListener, TriggerEventQueue> mTriggerListeners =
+ new HashMap<TriggerEventListener, TriggerEventQueue>();
- // Common pool of sensor events.
- private static SensorEventPool sPool;
+ private static final int MAX_EVENTS = 16;
+ private static Pools.SynchronizedPool<SensorEvent> sSensorEventPool;
+ private static Pools.SynchronizedPool<TriggerEvent> sTriggerEventPool;
// Looper associated with the context in which this instance was created.
private final Looper mMainLooper;
- // maps a SensorEventListener to a SensorEventQueue
- private final Hashtable<SensorEventListener, SensorEventQueue> mSensorEventQueueMap;
-
/** {@hide} */
public SystemSensorManager(Looper mainLooper) {
mMainLooper = mainLooper;
- mSensorEventQueueMap = new Hashtable<SensorEventListener, SensorEventQueue>();
-
synchronized(sSensorModuleLock) {
if (!sSensorModuleInitialized) {
sSensorModuleInitialized = true;
@@ -81,7 +81,10 @@
}
} while (i>0);
- sPool = new SensorEventPool( sFullSensorsList.size()*2 );
+ sSensorEventPool = new Pools.SynchronizedPool<SensorEvent>(
+ sFullSensorsList.size()*2);
+ sTriggerEventPool = new Pools.SynchronizedPool<TriggerEvent>(
+ sFullSensorsList.size()*2);
}
}
}
@@ -102,128 +105,133 @@
// Invariants to preserve:
// - one Looper per SensorEventListener
// - one Looper per SensorEventQueue
- // We map SensorEventListeners to a SensorEventQueue, which holds the looper
+ // We map SensorEventListener to a SensorEventQueue, which holds the looper
+ if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
- if (sensor == null) throw new NullPointerException("sensor cannot be null");
+ // Trigger Sensors should use the requestTriggerSensor call.
+ if (Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT) return false;
- boolean result;
- synchronized (mSensorEventQueueMap) {
- // check if we already have this SensorEventListener, Sensor pair
- // registered -- if so, we ignore the register. This is not ideal
- // but this is what the implementation has always been doing.
- for (SensorEventListenerSensorPair l : mListenerDelegates) {
- if (l.isSameListenerSensorPair(listener, sensor)) {
- // already added, just return silently.
- return true;
- }
- }
-
- // now find the SensorEventQueue associated to this listener
- SensorEventQueue queue = mSensorEventQueueMap.get(listener);
- if (queue != null) {
- result = queue.addSensor(sensor, delay);
- if (result) {
- // create a new ListenerDelegate for this pair
- mListenerDelegates.add(new SensorEventListenerSensorPair(listener, sensor));
- }
- } else {
+ synchronized (mSensorListeners) {
+ SensorEventQueue queue = mSensorListeners.get(listener);
+ if (queue == null) {
Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
- queue = new SensorEventQueue(listener, looper.getQueue());
- result = queue.addSensor(sensor, delay);
- if (result) {
- // create a new ListenerDelegate for this pair
- mListenerDelegates.add(new SensorEventListenerSensorPair(listener, sensor));
- mSensorEventQueueMap.put(listener, queue);
- } else {
+ queue = new SensorEventQueue(listener, looper);
+ if (!queue.addSensor(sensor, delay)) {
queue.dispose();
+ return false;
}
+ mSensorListeners.put(listener, queue);
+ return true;
+ } else {
+ return queue.addSensor(sensor, delay);
}
}
- return result;
}
/** @hide */
@Override
protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
- synchronized (mSensorEventQueueMap) {
+ // Trigger Sensors should use the cancelTriggerSensor call.
+ if (sensor != null && Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT) {
+ return;
+ }
- // remove this listener/sensor from our list
- final ArrayList<SensorEventListenerSensorPair> copy =
- new ArrayList<SensorEventListenerSensorPair>(mListenerDelegates);
- int lastIndex = copy.size()-1;
- for (int i=lastIndex ; i>= 0 ; i--) {
- if (copy.get(i).isSameListenerSensorPair(listener, sensor)) {
- mListenerDelegates.remove(i);
- }
- }
-
- // find the SensorEventQueue associated to this SensorEventListener
- SensorEventQueue queue = mSensorEventQueueMap.get(listener);
+ synchronized (mSensorListeners) {
+ SensorEventQueue queue = mSensorListeners.get(listener);
if (queue != null) {
- if (sensor != null) {
- queue.removeSensor(sensor);
+ boolean result;
+ if (sensor == null) {
+ result = queue.removeAllSensors();
} else {
- queue.removeAllSensors();
+ result = queue.removeSensor(sensor);
}
- if (!queue.hasSensors()) {
- mSensorEventQueueMap.remove(listener);
+ if (result && !queue.hasSensors()) {
+ mSensorListeners.remove(listener);
queue.dispose();
}
}
}
}
+ /** @hide */
+ @Override
+ protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
+ if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
- /*
- * ListenerDelegate is essentially a SensorEventListener, Sensor pair
- * and is associated with a single SensorEventQueue.
- */
- private static final class SensorEventListenerSensorPair {
- private final SensorEventListener mSensorEventListener;
- private final Sensor mSensor;
- public SensorEventListenerSensorPair(SensorEventListener listener, Sensor sensor) {
- mSensorEventListener = listener;
- mSensor = sensor;
- }
- public boolean isSameListenerSensorPair(SensorEventListener listener, Sensor sensor) {
- // if sensor is null, we match only on the listener
- if (sensor != null) {
- return (listener == mSensorEventListener) &&
- (sensor.getHandle() == mSensor.getHandle());
+ if (Sensor.getReportingMode(sensor) != Sensor.REPORTING_MODE_ONE_SHOT) return false;
+
+ synchronized (mTriggerListeners) {
+ TriggerEventQueue queue = mTriggerListeners.get(listener);
+ if (queue == null) {
+ queue = new TriggerEventQueue(listener, mMainLooper, this);
+ if (!queue.addSensor(sensor, 0)) {
+ queue.dispose();
+ return false;
+ }
+ mTriggerListeners.put(listener, queue);
+ return true;
} else {
- return (listener == mSensorEventListener);
+ return queue.addSensor(sensor, 0);
}
}
}
+ /** @hide */
+ @Override
+ protected boolean cancelTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
+ if (sensor != null && Sensor.getReportingMode(sensor) != Sensor.REPORTING_MODE_ONE_SHOT) {
+ return false;
+ }
+ synchronized (mTriggerListeners) {
+ TriggerEventQueue queue = mTriggerListeners.get(listener);
+ if (queue != null) {
+ boolean result;
+ if (sensor == null) {
+ result = queue.removeAllSensors();
+ } else {
+ result = queue.removeSensor(sensor);
+ }
+ if (result && !queue.hasSensors()) {
+ mTriggerListeners.remove(listener);
+ queue.dispose();
+ }
+ return result;
+ }
+ return false;
+ }
+ }
+
/*
- * SensorEventQueue is the communication channel with the sensor service,
- * there is a one-to-one mapping between SensorEventQueue and
- * SensorEventListener.
+ * 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.
*/
- private static final class SensorEventQueue {
- private static native int nativeInitSensorEventQueue(SensorEventQueue eventQ, MessageQueue msgQ, float[] scratch);
+ private static abstract class BaseEventQueue {
+ private native int nativeInitBaseEventQueue(BaseEventQueue eventQ, MessageQueue msgQ,
+ float[] scratch);
private static native int nativeEnableSensor(int eventQ, int handle, int us);
private static native int nativeDisableSensor(int eventQ, int handle);
private static native void nativeDestroySensorEventQueue(int eventQ);
private int nSensorEventQueue;
- private final SensorEventListener mListener;
private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
- private final SparseIntArray mSensorAccuracies = new SparseIntArray();
- private final SparseBooleanArray mFirstEvent = new SparseBooleanArray();
+ protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
+ protected final SparseBooleanArray mFirstEvent = new SparseBooleanArray();
private final CloseGuard mCloseGuard = CloseGuard.get();
private final float[] mScratch = new float[16];
- public SensorEventQueue(SensorEventListener listener, MessageQueue msgQ) {
- nSensorEventQueue = nativeInitSensorEventQueue(this, msgQ, mScratch);
- mListener = listener;
+ BaseEventQueue(Looper looper) {
+ nSensorEventQueue = nativeInitBaseEventQueue(this, looper.getQueue(), mScratch);
mCloseGuard.open("dispose");
}
+
public void dispose() {
dispose(false);
}
public boolean addSensor(Sensor sensor, int delay) {
+ // Check if already present.
+ if (mActiveSensors.get(sensor.getHandle())) return false;
+
if (enableSensor(sensor, delay) == 0) {
mActiveSensors.put(sensor.getHandle(), true);
return true;
@@ -231,7 +239,7 @@
return false;
}
- public void removeAllSensors() {
+ public boolean removeAllSensors() {
for (int i=0 ; i<mActiveSensors.size(); i++) {
if (mActiveSensors.valueAt(i) == true) {
int handle = mActiveSensors.keyAt(i);
@@ -244,21 +252,24 @@
}
}
}
+ return true;
}
- public void removeSensor(Sensor sensor) {
+ public boolean removeSensor(Sensor sensor) {
final int handle = sensor.getHandle();
if (mActiveSensors.get(handle)) {
disableSensor(sensor);
mActiveSensors.put(sensor.getHandle(), false);
+ return true;
}
+ return false;
}
public boolean hasSensors() {
// no more sensors are set
return mActiveSensors.indexOfValue(true) >= 0;
}
-
+
@Override
protected void finalize() throws Throwable {
try {
@@ -291,17 +302,30 @@
if (sensor == null) throw new NullPointerException();
return nativeDisableSensor(nSensorEventQueue, sensor.getHandle());
}
+ protected abstract void dispatchSensorEvent(int handle, float[] values, int accuracy,
+ long timestamp);
+ }
+
+ static final class SensorEventQueue extends BaseEventQueue {
+ private final SensorEventListener mListener;
+
+ public SensorEventQueue(SensorEventListener listener, Looper looper) {
+ super(looper);
+ mListener = listener;
+ }
// Called from native code.
@SuppressWarnings("unused")
- private void dispatchSensorEvent(int handle, float[] values, int inAccuracy, long timestamp) {
- // this is always called on the same thread.
- final SensorEvent t = sPool.getFromPool();
+ @Override
+ protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy,
+ long timestamp) {
+ final Sensor sensor = sHandleToSensor.get(handle);
+ SensorEvent t = sSensorEventPool.acquire();
+ if (t == null) t = new SensorEvent(MAX_EVENTS);
try {
- final Sensor sensor = sHandleToSensor.get(handle);
- final SensorEventListener listener = mListener;
- // FIXME: handle more than 3 values
- System.arraycopy(values, 0, t.values, 0, 3);
+ // Copy the entire values array.
+ // Any changes in length will be handled at the native layer.
+ System.arraycopy(values, 0, t.values, 0, t.values.length);
t.timestamp = timestamp;
t.accuracy = inAccuracy;
t.sensor = sensor;
@@ -313,72 +337,57 @@
final int accuracy = mSensorAccuracies.get(handle);
if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
mSensorAccuracies.put(handle, t.accuracy);
- listener.onAccuracyChanged(t.sensor, t.accuracy);
+ mListener.onAccuracyChanged(t.sensor, t.accuracy);
}
break;
default:
// For other sensors, just report the accuracy once
if (mFirstEvent.get(handle) == false) {
mFirstEvent.put(handle, true);
- listener.onAccuracyChanged(
+ mListener.onAccuracyChanged(
t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
}
break;
}
- listener.onSensorChanged(t);
+ mListener.onSensorChanged(t);
} finally {
- sPool.returnToPool(t);
+ sSensorEventPool.release(t);
}
}
}
- /*
- * A dumb pool of SensorEvent
- */
- private static final class SensorEventPool {
- private final int mPoolSize;
- private final SensorEvent mPool[];
- private int mNumItemsInPool;
+ static final class TriggerEventQueue extends BaseEventQueue {
+ private final TriggerEventListener mListener;
+ private SensorManager mManager;
- private SensorEvent createSensorEvent() {
- // maximal size for all legacy events is 3
- return new SensorEvent(3);
+ public TriggerEventQueue(TriggerEventListener listener, Looper looper,
+ SensorManager manager) {
+ super(looper);
+ mListener = listener;
+ mManager = manager;
}
- SensorEventPool(int poolSize) {
- mPoolSize = poolSize;
- mNumItemsInPool = poolSize;
- mPool = new SensorEvent[poolSize];
- }
+ // Called from native code.
+ @SuppressWarnings("unused")
+ @Override
+ protected void dispatchSensorEvent(int handle, float[] values, int accuracy, long timestamp) {
+ final Sensor sensor = sHandleToSensor.get(handle);
+ TriggerEvent t = sTriggerEventPool.acquire();
+ if (t == null) t = new TriggerEvent(MAX_EVENTS);
- SensorEvent getFromPool() {
- SensorEvent t = null;
- synchronized (this) {
- if (mNumItemsInPool > 0) {
- // remove the "top" item from the pool
- final int index = mPoolSize - mNumItemsInPool;
- t = mPool[index];
- mPool[index] = null;
- mNumItemsInPool--;
- }
- }
- if (t == null) {
- // the pool was empty or this item was removed from the pool for
- // the first time. In any case, we need to create a new item.
- t = createSensorEvent();
- }
- return t;
- }
+ try {
+ // Copy the entire values array.
+ // Any changes in length will be handled at the native layer.
+ System.arraycopy(values, 0, t.values, 0, t.values.length);
+ t.timestamp = timestamp;
+ t.sensor = sensor;
- void returnToPool(SensorEvent t) {
- synchronized (this) {
- // is there space left in the pool?
- if (mNumItemsInPool < mPoolSize) {
- // if so, return the item to the pool
- mNumItemsInPool++;
- final int index = mPoolSize - mNumItemsInPool;
- mPool[index] = t;
- }
+ // A trigger sensor should be auto disabled.
+ mManager.cancelTriggerSensorImpl(mListener, sensor);
+
+ mListener.onTrigger(t);
+ } finally {
+ sTriggerEventPool.release(t);
}
}
}
diff --git a/core/java/android/hardware/TriggerEvent.java b/core/java/android/hardware/TriggerEvent.java
new file mode 100644
index 0000000..bdd39f3
--- /dev/null
+++ b/core/java/android/hardware/TriggerEvent.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 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 android.hardware;
+
+/**
+ * This class represents a Trigger Event - the event
+ * associated with a Trigger Sensor. When the sensor detects a trigger
+ * event condition, such as significant motion in the case of the
+ * {@link Sensor#TYPE_SIGNIFICANT_MOTION}, the {@link TriggerEventListener}
+ * is called with the TriggerEvent. The sensor is automatically canceled
+ * after the trigger.
+ * <p>
+ * This class holds information such as the value of the sensor
+ * when the trigger happened, the timestamp along with detailed
+ * information regarding the Sensor itself.
+ * </p>
+ * @see android.hardware.SensorManager
+ * @see android.hardware.TriggerEvent
+ * @see android.hardware.Sensor
+ */
+public final class TriggerEvent {
+ /**
+ * <p>
+ * The length and contents of the {@link #values values} array depends on
+ * which {@link android.hardware.Sensor sensor} type is being monitored (see
+ * also {@link SensorEvent} for a definition of the coordinate system used).
+ * </p>
+ * <h4> {@link Sensor#TYPE_SIGNIFICANT_MOTION} </h4>
+ * The value field is of length 1. value[0] = 1.0 when the sensor triggers.
+ * 1.0 is the only allowed value.
+ */
+ public final float[] values;
+
+ /**
+ * The sensor that generated this event. See
+ * {@link android.hardware.SensorManager SensorManager} for details.
+ */
+ public Sensor sensor;
+
+ /**
+ * The time in nanosecond at which the event happened
+ */
+ public long timestamp;
+
+ TriggerEvent(int size) {
+ values = new float[size];
+ }
+}
diff --git a/core/java/android/hardware/TriggerEventListener.java b/core/java/android/hardware/TriggerEventListener.java
new file mode 100644
index 0000000..82b8907
--- /dev/null
+++ b/core/java/android/hardware/TriggerEventListener.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2013 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 android.hardware;
+
+/**
+ * This class is the listener used to handle Trigger Sensors.
+ * Trigger Sensors are sensors that trigger an event and are automatically
+ * disabled. {@link Sensor#TYPE_SIGNIFICANT_MOTION} is one such example.
+ * <p>
+ * SensorManager lets you access the device's {@link android.hardware.Sensor
+ * sensors}. Get an instance of this class by calling
+ * {@link android.content.Context#getSystemService(java.lang.String)
+ * Context.getSystemService()} with the argument
+ * {@link android.content.Context#SENSOR_SERVICE}.
+ * Usage details are explained in the example below.
+ * </p>
+ *
+ * <pre class="prettyprint">
+ * class TriggerListener extends TriggerEventListener {
+ * @Override
+ * public void onTrigger(TriggerEvent event) {
+ * // Do Work.
+ *
+ * // As it is a one shot sensor, it will be canceled automatically.
+ * // SensorManager.requestTriggerSensor(this, mSigMotion); needs to
+ * // be called again, if needed.
+ * }
+ * }
+ * public class SensorActivity extends Activity {
+ * private final SensorManager mSensorManager;
+ * private final Sensor mSigMotion;
+ * private final TriggerEventListener mListener = new TriggerEventListener();
+ *
+ * public SensorActivity() {
+ * mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
+ * mSigMotion = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
+ * }
+ *
+ * protected void onResume() {
+ * super.onResume();
+ * mSensorManager.requestTriggerSensor(mListener, mSigMotion);
+ * }
+ *
+ * protected void onPause() {
+ * super.onPause();
+ * // Call disable to ensure that the trigger request has been canceled.
+ * mSensorManager.cancelTriggerSensor(mListener, mSigMotion);
+ * }
+ *
+ * }
+ * </pre>
+ *
+ * @see TriggerEvent
+ * @see Sensor
+ */
+public abstract class TriggerEventListener {
+ /**
+ * The method that will be called when the sensor
+ * is triggered. Override this method in your implementation
+ * of this class.
+ *
+ * @param event The details of the event.
+ */
+ public abstract void onTrigger(TriggerEvent event);
+}
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 3083cb1..6374494 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -31,7 +31,7 @@
static struct {
jclass clazz;
jmethodID dispatchSensorEvent;
-} gSensorEventQueueClassInfo;
+} gBaseEventQueueClassInfo;
namespace android {
@@ -145,7 +145,7 @@
env->SetFloatArrayRegion(mScratch, 0, 16, buffer[i].data);
env->CallVoidMethod(mReceiverObject,
- gSensorEventQueueClassInfo.dispatchSensorEvent,
+ gBaseEventQueueClassInfo.dispatchSensorEvent,
buffer[i].sensor,
mScratch,
buffer[i].vector.status,
@@ -209,9 +209,9 @@
(void*)nativeGetNextSensor },
};
-static JNINativeMethod gSensorEventQueueMethods[] = {
- {"nativeInitSensorEventQueue",
- "(Landroid/hardware/SystemSensorManager$SensorEventQueue;Landroid/os/MessageQueue;[F)I",
+static JNINativeMethod gBaseEventQueueMethods[] = {
+ {"nativeInitBaseEventQueue",
+ "(Landroid/hardware/SystemSensorManager$BaseEventQueue;Landroid/os/MessageQueue;[F)I",
(void*)nativeInitSensorEventQueue },
{"nativeEnableSensor",
@@ -245,13 +245,13 @@
jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager",
gSystemSensorManagerMethods, NELEM(gSystemSensorManagerMethods));
- jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager$SensorEventQueue",
- gSensorEventQueueMethods, NELEM(gSensorEventQueueMethods));
+ jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager$BaseEventQueue",
+ gBaseEventQueueMethods, NELEM(gBaseEventQueueMethods));
- FIND_CLASS(gSensorEventQueueClassInfo.clazz, "android/hardware/SystemSensorManager$SensorEventQueue");
+ FIND_CLASS(gBaseEventQueueClassInfo.clazz, "android/hardware/SystemSensorManager$BaseEventQueue");
- GET_METHOD_ID(gSensorEventQueueClassInfo.dispatchSensorEvent,
- gSensorEventQueueClassInfo.clazz,
+ GET_METHOD_ID(gBaseEventQueueClassInfo.dispatchSensorEvent,
+ gBaseEventQueueClassInfo.clazz,
"dispatchSensorEvent", "(I[FIJ)V");
return 0;