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: Ifac5c0024c8e5f88b721e5cd97ff26afaaa36717
This commit is contained in:
Jaikumar Ganesh
2013-02-12 16:31:32 -08:00
parent f8a67f4f5d
commit 9a8df4dcf9
8 changed files with 553 additions and 222 deletions

View File

@@ -10296,16 +10296,20 @@ package android.hardware {
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
}
@@ -10327,6 +10331,7 @@ package android.hardware {
}
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);
@@ -10342,6 +10347,7 @@ package android.hardware {
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);
@@ -10405,6 +10411,17 @@ package android.hardware {
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 {

View File

@@ -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() {
}

View File

@@ -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(&#952/2)
* </p>
* <p>
* values[1]: y*sin(&#952/2)
* </p>
* <p>
* values[2]: z*sin(&#952/2)
* </p>
* <p>
* values[3]: cos(&#952/2) <i>(optional: only if value.length = 4)</i>
* </p>
* <li> values[0]: x*sin(&#952/2) </li>
* <li> values[1]: y*sin(&#952/2) </li>
* <li> values[2]: z*sin(&#952/2) </li>
* <li> values[3]: cos(&#952/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];
}

View File

@@ -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.
@@ -584,6 +601,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);
@@ -593,6 +611,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.
@@ -623,6 +647,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) {
@@ -1310,6 +1335,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) {

View File

@@ -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
// 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 @@ 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;
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);
}
}
}

View File

@@ -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];
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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 {
* 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);
}

View File

@@ -31,7 +31,7 @@
static struct {
jclass clazz;
jmethodID dispatchSensorEvent;
} gSensorEventQueueClassInfo;
} gBaseEventQueueClassInfo;
namespace android {
@@ -145,7 +145,7 @@ private:
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 @@ static JNINativeMethod gSystemSensorManagerMethods[] = {
(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 @@ int register_android_hardware_SensorManager(JNIEnv *env)
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;