diff options
| author | Jaikumar Ganesh <jaikumar@google.com> | 2013-02-12 16:31:32 -0800 |
|---|---|---|
| committer | Jaikumar Ganesh <jaikumar@google.com> | 2013-03-12 10:50:41 -0700 |
| commit | bc03a348d78326dee779a8ecc325799c9fceac98 (patch) | |
| tree | 9166e3fd7845731e16a882ea9b58a665bdee29f1 /core/java | |
| parent | e30f16df473a3323b19615f5f2ab2dd670a438da (diff) | |
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
Diffstat (limited to 'core/java')
| -rw-r--r-- | core/java/android/hardware/Sensor.java | 82 | ||||
| -rw-r--r-- | core/java/android/hardware/SensorEvent.java | 122 | ||||
| -rw-r--r-- | core/java/android/hardware/SensorManager.java | 89 | ||||
| -rw-r--r-- | core/java/android/hardware/SystemSensorManager.java | 309 | ||||
| -rw-r--r-- | core/java/android/hardware/TriggerEvent.java | 62 | ||||
| -rw-r--r-- | core/java/android/hardware/TriggerEventListener.java | 79 |
6 files changed, 529 insertions, 214 deletions
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index 41384d2209c1..af4c074742a7 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -114,11 +114,90 @@ public final class Sensor { /** 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 @@ public final class Sensor { private float mPower; private int mMinDelay; - Sensor() { } diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java index 51a17c1707f1..84c9131d1e1d 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 @@ public class SensorEvent { * 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 @@ public class SensorEvent { * 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 @@ public class SensorEvent { * </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 @@ public class SensorEvent { * </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 @@ public class SensorEvent { * <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 @@ public class SensorEvent { * </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 class SensorEvent { */ 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 c0d2fae25665..ce7bc7e6f6a2 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -38,7 +38,11 @@ import java.util.List; * 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 @@ public abstract class SensorManager { /** * 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 @@ public abstract class SensorManager { * @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 @@ public abstract class SensorManager { * 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 @@ public abstract class SensorManager { * @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 @@ public abstract class SensorManager { * 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 @@ public abstract class SensorManager { * @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 @@ public abstract class SensorManager { 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 95916310f9b8..3c231e59ebb0 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 @@ public class SystemSensorManager extends SensorManager { 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 @@ public class SystemSensorManager extends SensorManager { } } 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 @@ public class SystemSensorManager extends SensorManager { // Invariants to preserve: // - one Looper per SensorEventListener // - one Looper per SensorEventQueue - // We map SensorEventListeners to a SensorEventQueue, which holds the looper - - if (sensor == null) throw new NullPointerException("sensor cannot be null"); - - 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; - } - } + // We map SensorEventListener to a SensorEventQueue, which holds the looper + if (sensor == null) throw new IllegalArgumentException("sensor cannot be null"); - // 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 { + // Trigger Sensors should use the requestTriggerSensor call. + if (Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT) return false; + + 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) { - - // 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); - } - } + // Trigger Sensors should use the cancelTriggerSensor call. + if (sensor != null && Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT) { + return; + } - // 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"); + + if (Sensor.getReportingMode(sensor) != Sensor.REPORTING_MODE_ONE_SHOT) return false; - /* - * 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()); + 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 @@ public class SystemSensorManager extends SensorManager { 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 @@ public class SystemSensorManager extends SensorManager { } } } + 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 @@ public class SystemSensorManager extends SensorManager { 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 @@ public class SystemSensorManager extends SensorManager { 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; - - private SensorEvent createSensorEvent() { - // maximal size for all legacy events is 3 - return new SensorEvent(3); - } + static final class TriggerEventQueue extends BaseEventQueue { + private final TriggerEventListener mListener; + private SensorManager mManager; - SensorEventPool(int poolSize) { - mPoolSize = poolSize; - mNumItemsInPool = poolSize; - mPool = new SensorEvent[poolSize]; + public TriggerEventQueue(TriggerEventListener listener, Looper looper, + SensorManager manager) { + super(looper); + mListener = listener; + mManager = manager; } - 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; - } + // 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); - 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; - } + 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; + + // 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 000000000000..bdd39f39f688 --- /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 000000000000..82b8907f7b69 --- /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); +} |
