summaryrefslogtreecommitdiffstats
path: root/core/java/android/hardware/SensorManager.java
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:05:43 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:05:43 -0800
commitf013e1afd1e68af5e3b868c26a653bbfb39538f8 (patch)
tree7ad6c8fd9c7b55f4b4017171dec1cb760bbd26bf /core/java/android/hardware/SensorManager.java
parente70cfafe580c6f2994c4827cd8a534aabf3eb05c (diff)
downloadframeworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.zip
frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.gz
frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.bz2
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'core/java/android/hardware/SensorManager.java')
-rw-r--r--core/java/android/hardware/SensorManager.java1366
1 files changed, 1093 insertions, 273 deletions
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 9b88fff..f02094e 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -26,6 +26,7 @@ import android.os.Handler;
import android.os.Message;
import android.os.ServiceManager;
import android.util.Log;
+import android.util.SparseArray;
import android.view.IRotationWatcher;
import android.view.IWindowManager;
import android.view.Surface;
@@ -33,7 +34,9 @@ import android.view.Surface;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
/**
* Class that lets you access the device's sensors. Get an instance of this
@@ -43,126 +46,119 @@ import java.util.Arrays;
public class SensorManager extends IRotationWatcher.Stub
{
private static final String TAG = "SensorManager";
+ private static final float[] mTempMatrix = new float[16];
- /** NOTE: sensor IDs must be a power of 2 */
+ /* NOTE: sensor IDs must be a power of 2 */
- /** A constant describing an orientation sensor.
- * Sensor values are yaw, pitch and roll
- *
- * Yaw is the compass heading in degrees, range [0, 360[
- * 0 = North, 90 = East, 180 = South, 270 = West
- *
- * Pitch indicates the tilt of the top of the device,
- * with range -90 to 90.
- * Positive values indicate that the bottom of the device is tilted up
- * and negative values indicate the top of the device is tilted up.
- *
- * Roll indicates the side to side tilt of the device,
- * with range -90 to 90.
- * Positive values indicate that the left side of the device is tilted up
- * and negative values indicate the right side of the device is tilted up.
+ /**
+ * A constant describing an orientation sensor.
+ * See {@link android.hardware.SensorListener SensorListener} for more details.
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
+ @Deprecated
public static final int SENSOR_ORIENTATION = 1 << 0;
- /** A constant describing an accelerometer.
- * Sensor values are acceleration in the X, Y and Z axis,
- * where the X axis has positive direction toward the right side of the device,
- * the Y axis has positive direction toward the top of the device
- * and the Z axis has positive direction toward the front of the device.
- *
- * The direction of the force of gravity is indicated by acceleration values in the
- * X, Y and Z axes. The typical case where the device is flat relative to the surface
- * of the Earth appears as -STANDARD_GRAVITY in the Z axis
- * and X and Z values close to zero.
- *
- * Acceleration values are given in SI units (m/s^2)
- *
+ /**
+ * A constant describing an accelerometer.
+ * See {@link android.hardware.SensorListener SensorListener} for more details.
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
+ @Deprecated
public static final int SENSOR_ACCELEROMETER = 1 << 1;
- /** A constant describing a temperature sensor
- * Only the first value is defined for this sensor and it
- * contains the ambient temperature in degree C.
+ /**
+ * A constant describing a temperature sensor
+ * See {@link android.hardware.SensorListener SensorListener} for more details.
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
+ @Deprecated
public static final int SENSOR_TEMPERATURE = 1 << 2;
- /** A constant describing a magnetic sensor
- * Sensor values are the magnetic vector in the X, Y and Z axis,
- * where the X axis has positive direction toward the right side of the device,
- * the Y axis has positive direction toward the top of the device
- * and the Z axis has positive direction toward the front of the device.
- *
- * Magnetic values are given in micro-Tesla (uT)
- *
+ /**
+ * A constant describing a magnetic sensor
+ * See {@link android.hardware.SensorListener SensorListener} for more details.
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
+ @Deprecated
public static final int SENSOR_MAGNETIC_FIELD = 1 << 3;
- /** A constant describing an ambient light sensor
- * Only the first value is defined for this sensor and it contains
- * the ambient light measure in lux.
- *
+ /**
+ * A constant describing an ambient light sensor
+ * See {@link android.hardware.SensorListener SensorListener} for more details.
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
+ @Deprecated
public static final int SENSOR_LIGHT = 1 << 4;
- /** A constant describing a proximity sensor
- * Only the first value is defined for this sensor and it contains
- * the distance between the sensor and the object in meters (m)
+ /**
+ * A constant describing a proximity sensor
+ * See {@link android.hardware.SensorListener SensorListener} for more details.
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
+ @Deprecated
public static final int SENSOR_PROXIMITY = 1 << 5;
- /** A constant describing a Tricorder
- * When this sensor is available and enabled, the device can be
- * used as a fully functional Tricorder. All values are returned in
- * SI units.
+ /**
+ * A constant describing a Tricorder
+ * See {@link android.hardware.SensorListener SensorListener} for more details.
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
+ @Deprecated
public static final int SENSOR_TRICORDER = 1 << 6;
- /** A constant describing an orientation sensor.
- * Sensor values are yaw, pitch and roll
- *
- * Yaw is the compass heading in degrees, 0 <= range < 360
- * 0 = North, 90 = East, 180 = South, 270 = West
- *
- * This is similar to SENSOR_ORIENTATION except the data is not
- * smoothed or filtered in any way.
+ /**
+ * A constant describing an orientation sensor.
+ * See {@link android.hardware.SensorListener SensorListener} for more details.
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
+ @Deprecated
public static final int SENSOR_ORIENTATION_RAW = 1 << 7;
/** A constant that includes all sensors */
+ @Deprecated
public static final int SENSOR_ALL = 0x7F;
/** Smallest sensor ID */
+ @Deprecated
public static final int SENSOR_MIN = SENSOR_ORIENTATION;
/** Largest sensor ID */
+ @Deprecated
public static final int SENSOR_MAX = ((SENSOR_ALL + 1)>>1);
- /** Index of the X value in the array returned by
+ /** Index of the X value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged} */
+ @Deprecated
public static final int DATA_X = 0;
- /** Index of the Y value in the array returned by
+ /** Index of the Y value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged} */
+ @Deprecated
public static final int DATA_Y = 1;
- /** Index of the Z value in the array returned by
+ /** Index of the Z value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged} */
+ @Deprecated
public static final int DATA_Z = 2;
-
- /** Offset to the raw values in the array returned by
+
+ /** Offset to the untransformed values in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged} */
+ @Deprecated
public static final int RAW_DATA_INDEX = 3;
- /** Index of the raw X value in the array returned by
+ /** Index of the untransformed X value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged} */
+ @Deprecated
public static final int RAW_DATA_X = 3;
- /** Index of the raw X value in the array returned by
+ /** Index of the untransformed Y value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged} */
+ @Deprecated
public static final int RAW_DATA_Y = 4;
- /** Index of the raw X value in the array returned by
+ /** Index of the untransformed Z value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged} */
+ @Deprecated
public static final int RAW_DATA_Z = 5;
-
-
+
+
/** Standard gravity (g) on Earth. This value is equivalent to 1G */
public static final float STANDARD_GRAVITY = 9.80665f;
@@ -177,7 +173,7 @@ public class SensorManager extends IRotationWatcher.Stub
public static final float GRAVITY_JUPITER = 23.12f;
public static final float GRAVITY_SATURN = 8.96f;
public static final float GRAVITY_URANUS = 8.69f;
- public static final float GRAVITY_NEPTUN = 11.0f;
+ public static final float GRAVITY_NEPTUNE = 11.0f;
public static final float GRAVITY_PLUTO = 0.6f;
public static final float GRAVITY_DEATH_STAR_I = 0.000000353036145f;
public static final float GRAVITY_THE_ISLAND = 4.815162342f;
@@ -208,52 +204,84 @@ public class SensorManager extends IRotationWatcher.Stub
/** rate suitable for the user interface */
public static final int SENSOR_DELAY_UI = 2;
/** rate (default) suitable for screen orientation changes */
- public static final int SENSOR_DELAY_NORMAL = 3;
+ public static final int SENSOR_DELAY_NORMAL = 3;
+
-
/** The values returned by this sensor cannot be trusted, calibration
* is needed or the environment doesn't allow readings */
public static final int SENSOR_STATUS_UNRELIABLE = 0;
-
+
/** This sensor is reporting data with low accuracy, calibration with the
* environment is needed */
public static final int SENSOR_STATUS_ACCURACY_LOW = 1;
- /** This sensor is reporting data with an average level of accuracy,
+ /** This sensor is reporting data with an average level of accuracy,
* calibration with the environment may improve the readings */
public static final int SENSOR_STATUS_ACCURACY_MEDIUM = 2;
-
+
/** This sensor is reporting data with maximum accuracy */
public static final int SENSOR_STATUS_ACCURACY_HIGH = 3;
-
+ /** see {@link #remapCoordinateSystem} */
+ public static final int AXIS_X = 1;
+ /** see {@link #remapCoordinateSystem} */
+ public static final int AXIS_Y = 2;
+ /** see {@link #remapCoordinateSystem} */
+ public static final int AXIS_Z = 3;
+ /** see {@link #remapCoordinateSystem} */
+ public static final int AXIS_MINUS_X = AXIS_X | 0x80;
+ /** see {@link #remapCoordinateSystem} */
+ public static final int AXIS_MINUS_Y = AXIS_Y | 0x80;
+ /** see {@link #remapCoordinateSystem} */
+ public static final int AXIS_MINUS_Z = AXIS_Z | 0x80;
+
+ /*-----------------------------------------------------------------------*/
- private static final int SENSOR_DISABLE = -1;
- private static final int SENSOR_ORDER_MASK = 0x1F;
- private static final int SENSOR_STATUS_SHIFT = 28;
private ISensorService mSensorService;
- private Looper mLooper;
+ Looper mMainLooper;
+ @SuppressWarnings("deprecation")
+ private HashMap<SensorListener, LegacyListener> mLegacyListenersMap =
+ new HashMap<SensorListener, LegacyListener>();
- private static IWindowManager sWindowManager;
- private static int sRotation = 0;
+ /*-----------------------------------------------------------------------*/
- /* The thread and the sensor list are global to the process
+ private static final int SENSOR_DISABLE = -1;
+ private static boolean sSensorModuleInitialized = false;
+ private static ArrayList<Sensor> sFullSensorsList = new ArrayList<Sensor>();
+ private static SparseArray<List<Sensor>> sSensorListByType = new SparseArray<List<Sensor>>();
+ private static IWindowManager sWindowManager;
+ private static int sRotation = Surface.ROTATION_0;
+ /* The thread and the sensor list are global to the process
* but the actual thread is spawned on demand */
- static final private SensorThread sSensorThread = new SensorThread();
- static final private ArrayList<ListenerDelegate> sListeners =
+ private static SensorThread sSensorThread;
+
+ // Used within this module from outside SensorManager, don't make private
+ static SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>();
+ static final ArrayList<ListenerDelegate> sListeners =
new ArrayList<ListenerDelegate>();
+ /*-----------------------------------------------------------------------*/
static private class SensorThread {
- private Thread mThread;
+ Thread mThread;
+
+ SensorThread() {
+ // this gets to the sensor module. We can have only one per process.
+ sensors_data_init();
+ }
+
+ @Override
+ protected void finalize() {
+ sensors_data_uninit();
+ }
// must be called with sListeners lock
void startLocked(ISensorService service) {
try {
if (mThread == null) {
ParcelFileDescriptor fd = service.getDataChanel();
- mThread = new Thread(new SensorThreadRunnable(fd, service),
+ mThread = new Thread(new SensorThreadRunnable(fd),
SensorThread.class.getName());
mThread.start();
}
@@ -263,162 +291,165 @@ public class SensorManager extends IRotationWatcher.Stub
}
private class SensorThreadRunnable implements Runnable {
- private ISensorService mSensorService;
private ParcelFileDescriptor mSensorDataFd;
- private final byte mAccuracies[] = new byte[32];
- SensorThreadRunnable(ParcelFileDescriptor fd, ISensorService service) {
+ SensorThreadRunnable(ParcelFileDescriptor fd) {
mSensorDataFd = fd;
- mSensorService = service;
- Arrays.fill(mAccuracies, (byte)-1);
}
public void run() {
- int sensors_of_interest;
- float[] values = new float[6];
+ //Log.d(TAG, "entering main sensor thread");
+ final float[] values = new float[3];
+ final int[] status = new int[1];
+ final long timestamp[] = new long[1];
Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
- synchronized (sListeners) {
- _sensors_data_open(mSensorDataFd.getFileDescriptor());
- try {
- mSensorDataFd.close();
- } catch (IOException e) {
- // *shrug*
- Log.e(TAG, "IOException: ", e);
- }
- mSensorDataFd = null;
- //mSensorDataFd.
- // the first time, compute the sensors we need. this is not
- // a big deal if it changes by the time we call
- // _sensors_data_poll, it'll get recomputed for the next
- // round.
- sensors_of_interest = 0;
- final int size = sListeners.size();
- for (int i=0 ; i<size ; i++) {
- sensors_of_interest |= sListeners.get(i).mSensors;
- if ((sensors_of_interest & SENSOR_ALL) == SENSOR_ALL)
- break;
- }
+ if (mSensorDataFd == null) {
+ Log.e(TAG, "mSensorDataFd == NULL, exiting");
+ return;
}
+ // this thread is guaranteed to be unique
+ sensors_data_open(mSensorDataFd.getFileDescriptor());
+ try {
+ mSensorDataFd.close();
+ } catch (IOException e) {
+ // *shrug*
+ Log.e(TAG, "IOException: ", e);
+ }
+ mSensorDataFd = null;
+
while (true) {
// wait for an event
- final int sensor_result = _sensors_data_poll(values, sensors_of_interest);
- final int sensor_order = sensor_result & SENSOR_ORDER_MASK;
- final int sensor = 1 << sensor_result;
- int accuracy = sensor_result>>>SENSOR_STATUS_SHIFT;
-
- if ((sensors_of_interest & sensor)!=0) {
- // show the notification only if someone is listening for
- // this sensor
- if (accuracy != mAccuracies[sensor_order]) {
- try {
- mSensorService.reportAccuracy(sensor, accuracy);
- mAccuracies[sensor_order] = (byte)accuracy;
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in reportAccuracy: ", e);
- }
- } else {
- accuracy = -1;
- }
+ final int sensor = sensors_data_poll(values, status, timestamp);
+
+ if (sensor == -1) {
+ // we lost the connection to the event stream. this happens
+ // when the last listener is removed.
+ Log.d(TAG, "_sensors_data_poll() failed, we bail out.");
+ break;
}
-
+
+ int accuracy = status[0];
synchronized (sListeners) {
if (sListeners.isEmpty()) {
// we have no more listeners, terminate the thread
- _sensors_data_close();
+ sensors_data_close();
mThread = null;
break;
}
- // convert for the current screen orientation
- mapSensorDataToWindow(sensor, values, SensorManager.getRotation());
- // report the sensor event to all listeners that
- // care about it.
- sensors_of_interest = 0;
- final int size = sListeners.size();
- for (int i=0 ; i<size ; i++) {
- ListenerDelegate listener = sListeners.get(i);
- sensors_of_interest |= listener.mSensors;
- if (listener.hasSensor(sensor)) {
- // this is asynchronous (okay to call
- // with sListeners lock held.
- listener.onSensorChanged(sensor, values, accuracy);
+ final Sensor sensorObject = sHandleToSensor.get(sensor);
+ if (sensorObject != null) {
+ // report the sensor event to all listeners that
+ // care about it.
+ final int size = sListeners.size();
+ for (int i=0 ; i<size ; i++) {
+ ListenerDelegate listener = sListeners.get(i);
+ if (listener.hasSensor(sensorObject)) {
+ // this is asynchronous (okay to call
+ // with sListeners lock held).
+ listener.onSensorChangedLocked(sensorObject,
+ values, timestamp, accuracy);
+ }
}
}
}
}
+ //Log.d(TAG, "exiting main sensor thread");
}
}
}
- private class ListenerDelegate extends Binder {
-
- private SensorListener mListener;
- private int mSensors;
- private float[] mValuesPool;
+ /*-----------------------------------------------------------------------*/
- ListenerDelegate(SensorListener listener, int sensors) {
- mListener = listener;
- mSensors = sensors;
- mValuesPool = new float[6];
+ private class ListenerDelegate extends Binder {
+ final SensorEventListener mSensorEventListener;
+ private final ArrayList<Sensor> mSensorList = new ArrayList<Sensor>();
+ private final Handler mHandler;
+ private SensorEvent mValuesPool;
+ public int mSensors;
+
+ ListenerDelegate(SensorEventListener listener, Sensor sensor, Handler handler) {
+ mSensorEventListener = listener;
+ Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
+ // currently we create one Handler instance per listener, but we could
+ // have one per looper (we'd need to pass the ListenerDelegate
+ // instance to handleMessage and keep track of them separately).
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ SensorEvent t = (SensorEvent)msg.obj;
+ if (t.accuracy >= 0) {
+ mSensorEventListener.onAccuracyChanged(t.sensor, t.accuracy);
+ }
+ mSensorEventListener.onSensorChanged(t);
+ returnToPool(t);
+ }
+ };
+ addSensor(sensor);
}
- int addSensors(int sensors) {
- mSensors |= sensors;
- return mSensors;
- }
- int removeSensors(int sensors) {
- mSensors &= ~sensors;
- return mSensors;
- }
- boolean hasSensor(int sensor) {
- return ((mSensors & sensor) != 0);
+ protected SensorEvent createSensorEvent() {
+ // maximal size for all legacy events is 3
+ return new SensorEvent(3);
}
- void onSensorChanged(int sensor, float[] values, int accuracy) {
- float[] v;
+ protected SensorEvent getFromPool() {
+ SensorEvent t = null;
synchronized (this) {
// remove the array from the pool
- v = mValuesPool;
+ t = mValuesPool;
mValuesPool = null;
}
+ if (t == null) {
+ // the pool was empty, we need a new one
+ t = createSensorEvent();
+ }
+ return t;
+ }
- if (v != null) {
- v[0] = values[0];
- v[1] = values[1];
- v[2] = values[2];
- v[3] = values[3];
- v[4] = values[4];
- v[5] = values[5];
- } else {
- // the pool was empty, we need to dup the array
- v = values.clone();
+ protected void returnToPool(SensorEvent t) {
+ synchronized (this) {
+ // put back the array into the pool
+ if (mValuesPool == null) {
+ mValuesPool = t;
+ }
}
+ }
+
+ Object getListener() {
+ return mSensorEventListener;
+ }
+
+ int addSensor(Sensor sensor) {
+ mSensors |= 1<<sensor.getHandle();
+ mSensorList.add(sensor);
+ return mSensors;
+ }
+ int removeSensor(Sensor sensor) {
+ mSensors &= ~(1<<sensor.getHandle());
+ mSensorList.remove(sensor);
+ return mSensors;
+ }
+ boolean hasSensor(Sensor sensor) {
+ return ((mSensors & (1<<sensor.getHandle())) != 0);
+ }
+ List<Sensor> getSensors() {
+ return mSensorList;
+ }
+ void onSensorChangedLocked(Sensor sensor, float[] values, long[] timestamp, int accuracy) {
+ SensorEvent t = getFromPool();
+ final float[] v = t.values;
+ v[0] = values[0];
+ v[1] = values[1];
+ v[2] = values[2];
+ t.timestamp = timestamp[0];
+ t.accuracy = accuracy;
+ t.sensor = sensor;
Message msg = Message.obtain();
- msg.what = sensor;
- msg.obj = v;
- msg.arg1 = accuracy;
+ msg.what = 0;
+ msg.obj = t;
mHandler.sendMessage(msg);
}
-
- private final Handler mHandler = new Handler(mLooper) {
- @Override public void handleMessage(Message msg) {
- if (msg.arg1 >= 0) {
- try {
- mListener.onAccuracyChanged(msg.what, msg.arg1);
- } catch (AbstractMethodError e) {
- // old app that doesn't implement this method
- // just ignore it.
- }
- }
- mListener.onSensorChanged(msg.what, (float[])msg.obj);
- synchronized (this) {
- // put back the array into the pool
- if (mValuesPool == null) {
- mValuesPool = (float[])msg.obj;
- }
- }
- }
- };
}
/**
@@ -427,41 +458,157 @@ public class SensorManager extends IRotationWatcher.Stub
public SensorManager(Looper mainLooper) {
mSensorService = ISensorService.Stub.asInterface(
ServiceManager.getService(Context.SENSOR_SERVICE));
-
- sWindowManager = IWindowManager.Stub.asInterface(
- ServiceManager.getService("window"));
+ mMainLooper = mainLooper;
- if (sWindowManager != null) {
- // if it's null we're running in the system process
- // which won't get the rotated values
- try {
- sWindowManager.watchRotation(this);
- } catch (RemoteException e) {
+
+ synchronized(sListeners) {
+ if (!sSensorModuleInitialized) {
+ sSensorModuleInitialized = true;
+
+ nativeClassInit();
+
+ sWindowManager = IWindowManager.Stub.asInterface(
+ ServiceManager.getService("window"));
+ if (sWindowManager != null) {
+ // if it's null we're running in the system process
+ // which won't get the rotated values
+ try {
+ sRotation = sWindowManager.watchRotation(this);
+ } catch (RemoteException e) {
+ }
+ }
+
+ // initialize the sensor list
+ sensors_module_init();
+ final ArrayList<Sensor> fullList = sFullSensorsList;
+ int i = 0;
+ do {
+ Sensor sensor = new Sensor();
+ i = sensors_module_get_next_sensor(sensor, i);
+
+ if (i>=0) {
+ Log.d(TAG, "found sensor: " + sensor.getName() +
+ ", handle=" + sensor.getHandle());
+ sensor.setLegacyType(getLegacySensorType(sensor.getType()));
+ fullList.add(sensor);
+ sHandleToSensor.append(sensor.getHandle(), sensor);
+ }
+ } while (i>0);
+
+ sSensorThread = new SensorThread();
}
}
+ }
- mLooper = mainLooper;
+ private int getLegacySensorType(int type) {
+ switch (type) {
+ case Sensor.TYPE_ACCELEROMETER:
+ return SENSOR_ACCELEROMETER;
+ case Sensor.TYPE_MAGNETIC_FIELD:
+ return SENSOR_MAGNETIC_FIELD;
+ case Sensor.TYPE_ORIENTATION:
+ return SENSOR_ORIENTATION_RAW;
+ case Sensor.TYPE_TEMPERATURE:
+ return SENSOR_TEMPERATURE;
+ }
+ return 0;
}
- /** @return available sensors */
+ /** @return available sensors.
+ * @deprecated This method is deprecated, use
+ * {@link SensorManager#getSensorList(int)} instead
+ */
+ @Deprecated
public int getSensors() {
- return _sensors_data_get_sensors();
+ int result = 0;
+ final ArrayList<Sensor> fullList = sFullSensorsList;
+ for (Sensor i : fullList) {
+ switch (i.getType()) {
+ case Sensor.TYPE_ACCELEROMETER:
+ result |= SensorManager.SENSOR_ACCELEROMETER;
+ break;
+ case Sensor.TYPE_MAGNETIC_FIELD:
+ result |= SensorManager.SENSOR_MAGNETIC_FIELD;
+ break;
+ case Sensor.TYPE_ORIENTATION:
+ result |= SensorManager.SENSOR_ORIENTATION |
+ SensorManager.SENSOR_ORIENTATION_RAW;
+ break;
+ }
+ }
+ return result;
}
/**
+ * Use this method to get the list of available sensors of a certain
+ * type. Make multiple calls to get sensors of different types or use
+ * {@link android.hardware.Sensor#TYPE_ALL Sensor.TYPE_ALL} to get all
+ * the sensors.
+ *
+ * @param type of sensors requested
+ * @return a list of sensors matching the asked type.
+ */
+ public List<Sensor> getSensorList(int type) {
+ // cache the returned lists the first time
+ List<Sensor> list;
+ final ArrayList<Sensor> fullList = sFullSensorsList;
+ synchronized(fullList) {
+ list = sSensorListByType.get(type);
+ if (list == null) {
+ if (type == Sensor.TYPE_ALL) {
+ list = fullList;
+ } else {
+ list = new ArrayList<Sensor>();
+ for (Sensor i : fullList) {
+ if (i.getType() == type)
+ list.add(i);
+ }
+ }
+ list = Collections.unmodifiableList(list);
+ sSensorListByType.append(type, list);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Use this method to get the default sensor for a given type. Note that
+ * the returned sensor could be a composite sensor, and its data could be
+ * averaged or filtered. If you need to access the raw sensors use
+ * {@link SensorManager#getSensorList(int) getSensorList}.
+ *
+ *
+ * @param type of sensors requested
+ * @return the default sensors matching the asked type.
+ */
+ public Sensor getDefaultSensor(int type) {
+ // TODO: need to be smarter, for now, just return the 1st sensor
+ List<Sensor> l = getSensorList(type);
+ return l.isEmpty() ? null : l.get(0);
+ }
+
+
+ /**
* Registers a listener for given sensors.
+ * @deprecated This method is deprecated, use
+ * {@link SensorManager#registerListener(SensorEventListener, Sensor, int)}
+ * instead.
*
* @param listener sensor listener object
* @param sensors a bit masks of the sensors to register to
*
* @return true if the sensor is supported and successfully enabled
*/
+ @Deprecated
public boolean registerListener(SensorListener listener, int sensors) {
return registerListener(listener, sensors, SENSOR_DELAY_NORMAL);
}
/**
- * Registers a listener for given sensors.
+ * Registers a SensorListener for given sensors.
+ * @deprecated This method is deprecated, use
+ * {@link SensorManager#registerListener(SensorEventListener, Sensor, int)}
+ * instead.
*
* @param listener sensor listener object
* @param sensors a bit masks of the sensors to register to
@@ -471,9 +618,189 @@ public class SensorManager extends IRotationWatcher.Stub
*
* @return true if the sensor is supported and successfully enabled
*/
+ @Deprecated
public boolean registerListener(SensorListener listener, int sensors, int rate) {
- boolean result;
+ boolean result = false;
+ result = registerLegacyListener(SENSOR_ACCELEROMETER, Sensor.TYPE_ACCELEROMETER,
+ listener, sensors, rate) || result;
+ result = registerLegacyListener(SENSOR_MAGNETIC_FIELD, Sensor.TYPE_MAGNETIC_FIELD,
+ listener, sensors, rate) || result;
+ result = registerLegacyListener(SENSOR_ORIENTATION_RAW, Sensor.TYPE_ORIENTATION,
+ listener, sensors, rate) || result;
+ result = registerLegacyListener(SENSOR_ORIENTATION, Sensor.TYPE_ORIENTATION,
+ listener, sensors, rate) || result;
+ result = registerLegacyListener(SENSOR_TEMPERATURE, Sensor.TYPE_TEMPERATURE,
+ listener, sensors, rate) || result;
+ return result;
+ }
+ @SuppressWarnings("deprecation")
+ private boolean registerLegacyListener(int legacyType, int type,
+ SensorListener listener, int sensors, int rate)
+ {
+ boolean result = false;
+ // Are we activating this legacy sensor?
+ if ((sensors & legacyType) != 0) {
+ // if so, find a suitable Sensor
+ Sensor sensor = getDefaultSensor(type);
+ if (sensor != null) {
+ // If we don't already have one, create a LegacyListener
+ // to wrap this listener and process the events as
+ // they are expected by legacy apps.
+ LegacyListener legacyListener = null;
+ synchronized (mLegacyListenersMap) {
+ legacyListener = mLegacyListenersMap.get(listener);
+ if (legacyListener == null) {
+ // we didn't find a LegacyListener for this client,
+ // create one, and put it in our list.
+ legacyListener = new LegacyListener(listener);
+ mLegacyListenersMap.put(listener, legacyListener);
+ }
+ }
+ // register this legacy sensor with this legacy listener
+ legacyListener.registerSensor(legacyType);
+ // and finally, register the legacy listener with the new apis
+ result = registerListener(legacyListener, sensor, rate);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Unregisters a listener for the sensors with which it is registered.
+ * @deprecated This method is deprecated, use
+ * {@link SensorManager#unregisterListener(SensorEventListener, Sensor)}
+ * instead.
+ *
+ * @param listener a SensorListener object
+ * @param sensors a bit masks of the sensors to unregister from
+ */
+ @Deprecated
+ public void unregisterListener(SensorListener listener, int sensors) {
+ unregisterLegacyListener(SENSOR_ACCELEROMETER, Sensor.TYPE_ACCELEROMETER,
+ listener, sensors);
+ unregisterLegacyListener(SENSOR_MAGNETIC_FIELD, Sensor.TYPE_MAGNETIC_FIELD,
+ listener, sensors);
+ unregisterLegacyListener(SENSOR_ORIENTATION_RAW, Sensor.TYPE_ORIENTATION,
+ listener, sensors);
+ unregisterLegacyListener(SENSOR_ORIENTATION, Sensor.TYPE_ORIENTATION,
+ listener, sensors);
+ unregisterLegacyListener(SENSOR_TEMPERATURE, Sensor.TYPE_TEMPERATURE,
+ listener, sensors);
+ }
+
+ @SuppressWarnings("deprecation")
+ private void unregisterLegacyListener(int legacyType, int type,
+ SensorListener listener, int sensors)
+ {
+ // do we know about this listener?
+ LegacyListener legacyListener = null;
+ synchronized (mLegacyListenersMap) {
+ legacyListener = mLegacyListenersMap.get(listener);
+ }
+ if (legacyListener != null) {
+ // Are we deactivating this legacy sensor?
+ if ((sensors & legacyType) != 0) {
+ // if so, find the corresponding Sensor
+ Sensor sensor = getDefaultSensor(type);
+ if (sensor != null) {
+ // unregister this legacy sensor and if we don't
+ // need the corresponding Sensor, unregister it too
+ if (legacyListener.unregisterSensor(legacyType)) {
+ // corresponding sensor not needed, unregister
+ unregisterListener(legacyListener, sensor);
+ // finally check if we still need the legacyListener
+ // in our mapping, if not, get rid of it too.
+ synchronized(sListeners) {
+ boolean found = false;
+ for (ListenerDelegate i : sListeners) {
+ if (i.getListener() == legacyListener) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ synchronized (mLegacyListenersMap) {
+ mLegacyListenersMap.remove(listener);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Unregisters a listener for all sensors.
+ * @deprecated This method is deprecated, use
+ * {@link SensorManager#unregisterListener(SensorEventListener)}
+ * instead.
+ *
+ * @param listener a SensorListener object
+ */
+ @Deprecated
+ public void unregisterListener(SensorListener listener) {
+ unregisterListener(listener, SENSOR_ALL);
+ }
+
+ /**
+ * Unregisters a listener for the sensors with which it is registered.
+ *
+ * @param listener a SensorEventListener object
+ * @param sensor the sensor to unregister from
+ *
+ */
+ public void unregisterListener(SensorEventListener listener, Sensor sensor) {
+ unregisterListener((Object)listener, sensor);
+ }
+
+ /**
+ * Unregisters a listener for all sensors.
+ *
+ * @param listener a SensorListener object
+ *
+ */
+ public void unregisterListener(SensorEventListener listener) {
+ unregisterListener((Object)listener);
+ }
+
+
+ /**
+ * Registers a {@link android.hardware.SensorEventListener SensorEventListener}
+ * for the given sensor.
+ *
+ * @param listener A {@link android.hardware.SensorEventListener SensorEventListener} object.
+ * @param sensor The {@link android.hardware.Sensor Sensor} to register to.
+ * @param rate The rate {@link android.hardware.SensorEvent sensor events} are delivered at.
+ * This is only a hint to the system. Events may be received faster or
+ * slower than the specified rate. Usually events are received faster.
+ *
+ * @return true if the sensor is supported and successfully enabled.
+ *
+ */
+ public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate) {
+ return registerListener(listener, sensor, rate, null);
+ }
+
+ /**
+ * Registers a {@link android.hardware.SensorEventListener SensorEventListener}
+ * for the given sensor.
+ *
+ * @param listener A {@link android.hardware.SensorEventListener SensorEventListener} object.
+ * @param sensor The {@link android.hardware.Sensor Sensor} to register to.
+ * @param rate The rate {@link android.hardware.SensorEvent sensor events} are delivered at.
+ * This is only a hint to the system. Events may be received faster or
+ * slower than the specified rate. Usually events are received faster.
+ * @param handler The {@link android.os.Handler Handler} the
+ * {@link android.hardware.SensorEvent sensor events} will be delivered to.
+ *
+ * @return true if the sensor is supported and successfully enabled.
+ *
+ */
+ public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate,
+ Handler handler) {
+ boolean result;
int delay = -1;
switch (rate) {
case SENSOR_DELAY_FASTEST:
@@ -496,15 +823,15 @@ public class SensorManager extends IRotationWatcher.Stub
synchronized (sListeners) {
ListenerDelegate l = null;
for (ListenerDelegate i : sListeners) {
- if (i.mListener == listener) {
+ if (i.getListener() == listener) {
l = i;
break;
}
}
if (l == null) {
- l = new ListenerDelegate(listener, sensors);
- result = mSensorService.enableSensor(l, sensors, delay);
+ l = new ListenerDelegate(listener, sensor, handler);
+ result = mSensorService.enableSensor(l, sensor.getHandle(), delay);
if (result) {
sListeners.add(l);
sListeners.notify();
@@ -513,9 +840,9 @@ public class SensorManager extends IRotationWatcher.Stub
sSensorThread.startLocked(mSensorService);
}
} else {
- result = mSensorService.enableSensor(l, sensors, delay);
+ result = mSensorService.enableSensor(l, sensor.getHandle(), delay);
if (result) {
- l.addSensors(sensors);
+ l.addSensor(sensor);
}
}
}
@@ -526,25 +853,42 @@ public class SensorManager extends IRotationWatcher.Stub
return result;
}
- /**
- * Unregisters a listener for the sensors with which it is registered.
- *
- * @param listener a SensorListener object
- * @param sensors a bit masks of the sensors to unregister from
- */
- public void unregisterListener(SensorListener listener, int sensors) {
+ private void unregisterListener(Object listener, Sensor sensor) {
try {
synchronized (sListeners) {
final int size = sListeners.size();
for (int i=0 ; i<size ; i++) {
ListenerDelegate l = sListeners.get(i);
- if (l.mListener == listener) {
+ if (l.getListener() == listener) {
// disable these sensors
- mSensorService.enableSensor(l, sensors, SENSOR_DISABLE);
+ int handle = sensor.getHandle();
+ mSensorService.enableSensor(l, handle, SENSOR_DISABLE);
// if we have no more sensors enabled on this listener,
// take it off the list.
- if (l.removeSensors(sensors) == 0)
+ if (l.removeSensor(sensor) == 0) {
sListeners.remove(i);
+ }
+ break;
+ }
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in unregisterListener: ", e);
+ }
+ }
+
+ private void unregisterListener(Object listener) {
+ try {
+ synchronized (sListeners) {
+ final int size = sListeners.size();
+ for (int i=0 ; i<size ; i++) {
+ ListenerDelegate l = sListeners.get(i);
+ if (l.getListener() == listener) {
+ // disable all sensors for this listener
+ for (Sensor sensor : l.getSensors()) {
+ mSensorService.enableSensor(l, sensor.getHandle(), SENSOR_DISABLE);
+ }
+ sListeners.remove(i);
break;
}
}
@@ -555,65 +899,541 @@ public class SensorManager extends IRotationWatcher.Stub
}
/**
- * Unregisters a listener for all sensors.
+ * Computes the inclination matrix <b>I</b> as well as the rotation
+ * matrix <b>R</b> transforming a vector from the
+ * device coordinate system to the world's coordinate system which is
+ * defined as a direct orthonormal basis, where:
+ *
+ * <li>X is defined as the vector product <b>Y.Z</b> (It is tangential to
+ * the ground at the device's current location and roughly points East).</li>
+ * <li>Y is tangential to the ground at the device's current location and
+ * points towards the magnetic North Pole.</li>
+ * <li>Z points towards the sky and is perpendicular to the ground.</li>
+ * <p>
+ * <hr>
+ * <p>By definition:
+ * <p>[0 0 g] = <b>R</b> * <b>gravity</b> (g = magnitude of gravity)
+ * <p>[0 m 0] = <b>I</b> * <b>R</b> * <b>geomagnetic</b>
+ * (m = magnitude of geomagnetic field)
+ * <p><b>R</b> is the identity matrix when the device is aligned with the
+ * world's coordinate system, that is, when the device's X axis points
+ * toward East, the Y axis points to the North Pole and the device is facing
+ * the sky.
*
- * @param listener a SensorListener object
+ * <p><b>I</b> is a rotation matrix transforming the geomagnetic
+ * vector into the same coordinate space as gravity (the world's coordinate
+ * space). <b>I</b> is a simple rotation around the X axis.
+ * The inclination angle in radians can be computed with
+ * {@link #getInclination}.
+ * <hr>
+ *
+ * <p> Each matrix is returned either as a 3x3 or 4x4 row-major matrix
+ * depending on the length of the passed array:
+ * <p><u>If the array length is 16:</u>
+ * <pre>
+ * / M[ 0] M[ 1] M[ 2] M[ 3] \
+ * | M[ 4] M[ 5] M[ 6] M[ 7] |
+ * | M[ 8] M[ 9] M[10] M[11] |
+ * \ M[12] M[13] M[14] M[15] /
+ *</pre>
+ * This matrix is ready to be used by OpenGL ES's
+ * {@link javax.microedition.khronos.opengles.GL10#glLoadMatrixf(float[], int)
+ * glLoadMatrixf(float[], int)}.
+ * <p>Note that because OpenGL matrices are column-major matrices you must
+ * transpose the matrix before using it. However, since the matrix is a
+ * rotation matrix, its transpose is also its inverse, conveniently, it is
+ * often the inverse of the rotation that is needed for rendering; it can
+ * therefore be used with OpenGL ES directly.
+ * <p>
+ * Also note that the returned matrices always have this form:
+ * <pre>
+ * / M[ 0] M[ 1] M[ 2] 0 \
+ * | M[ 4] M[ 5] M[ 6] 0 |
+ * | M[ 8] M[ 9] M[10] 0 |
+ * \ 0 0 0 1 /
+ *</pre>
+ * <p><u>If the array length is 9:</u>
+ * <pre>
+ * / M[ 0] M[ 1] M[ 2] \
+ * | M[ 3] M[ 4] M[ 5] |
+ * \ M[ 6] M[ 7] M[ 8] /
+ *</pre>
+ *
+ * <hr>
+ * <p>The inverse of each matrix can be computed easily by taking its
+ * transpose.
+ *
+ * <p>The matrices returned by this function are meaningful only when the
+ * device is not free-falling and it is not close to the magnetic north.
+ * If the device is accelerating, or placed into a strong magnetic field,
+ * the returned matrices may be inaccurate.
+ *
+ * @param R is an array of 9 floats holding the rotation matrix <b>R</b>
+ * when this function returns. R can be null.<p>
+ * @param I is an array of 9 floats holding the rotation matrix <b>I</b>
+ * when this function returns. I can be null.<p>
+ * @param gravity is an array of 3 floats containing the gravity vector
+ * expressed in the device's coordinate. You can simply use the
+ * {@link android.hardware.SensorEvent#values values}
+ * returned by a {@link android.hardware.SensorEvent SensorEvent} of a
+ * {@link android.hardware.Sensor Sensor} of type
+ * {@link android.hardware.Sensor#TYPE_ACCELEROMETER TYPE_ACCELEROMETER}.<p>
+ * @param geomagnetic is an array of 3 floats containing the geomagnetic
+ * vector expressed in the device's coordinate. You can simply use the
+ * {@link android.hardware.SensorEvent#values values}
+ * returned by a {@link android.hardware.SensorEvent SensorEvent} of a
+ * {@link android.hardware.Sensor Sensor} of type
+ * {@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD TYPE_MAGNETIC_FIELD}.
+ * @return
+ * true on success<p>
+ * false on failure (for instance, if the device is in free fall).
+ * On failure the output matrices are not modified.
*/
- public void unregisterListener(SensorListener listener) {
- unregisterListener(listener, SENSOR_ALL);
+
+ public static boolean getRotationMatrix(float[] R, float[] I,
+ float[] gravity, float[] geomagnetic) {
+ // TODO: move this to native code for efficiency
+ float Ax = gravity[0];
+ float Ay = gravity[1];
+ float Az = gravity[2];
+ final float Ex = geomagnetic[0];
+ final float Ey = geomagnetic[1];
+ final float Ez = geomagnetic[2];
+ float Hx = Ey*Az - Ez*Ay;
+ float Hy = Ez*Ax - Ex*Az;
+ float Hz = Ex*Ay - Ey*Ax;
+ final float normH = (float)Math.sqrt(Hx*Hx + Hy*Hy + Hz*Hz);
+ if (normH < 0.1f) {
+ // device is close to free fall (or in space?), or close to
+ // magnetic north pole. Typical values are > 100.
+ return false;
+ }
+ final float invH = 1.0f / normH;
+ Hx *= invH;
+ Hy *= invH;
+ Hz *= invH;
+ final float invA = 1.0f / (float)Math.sqrt(Ax*Ax + Ay*Ay + Az*Az);
+ Ax *= invA;
+ Ay *= invA;
+ Az *= invA;
+ final float Mx = Ay*Hz - Az*Hy;
+ final float My = Az*Hx - Ax*Hz;
+ final float Mz = Ax*Hy - Ay*Hx;
+ if (R != null) {
+ if (R.length == 9) {
+ R[0] = Hx; R[1] = Hy; R[2] = Hz;
+ R[3] = Mx; R[4] = My; R[5] = Mz;
+ R[6] = Ax; R[7] = Ay; R[8] = Az;
+ } else if (R.length == 16) {
+ R[0] = Hx; R[1] = Hy; R[2] = Hz; R[3] = 0;
+ R[4] = Mx; R[5] = My; R[6] = Mz; R[7] = 0;
+ R[8] = Ax; R[9] = Ay; R[10] = Az; R[11] = 0;
+ R[12] = 0; R[13] = 0; R[14] = 0; R[15] = 1;
+ }
+ }
+ if (I != null) {
+ // compute the inclination matrix by projecting the geomagnetic
+ // vector onto the Z (gravity) and X (horizontal component
+ // of geomagnetic vector) axes.
+ final float invE = 1.0f / (float)Math.sqrt(Ex*Ex + Ey*Ey + Ez*Ez);
+ final float c = (Ex*Mx + Ey*My + Ez*Mz) * invE;
+ final float s = (Ex*Ax + Ey*Ay + Ez*Az) * invE;
+ if (I.length == 9) {
+ I[0] = 1; I[1] = 0; I[2] = 0;
+ I[3] = 0; I[4] = c; I[5] = s;
+ I[6] = 0; I[7] =-s; I[8] = c;
+ } else if (I.length == 16) {
+ I[0] = 1; I[1] = 0; I[2] = 0;
+ I[4] = 0; I[5] = c; I[6] = s;
+ I[8] = 0; I[9] =-s; I[10]= c;
+ I[3] = I[7] = I[11] = I[12] = I[13] = I[14] = 0;
+ I[15] = 1;
+ }
+ }
+ return true;
}
+ /**
+ * Computes the geomagnetic inclination angle in radians from the
+ * inclination matrix <b>I</b> returned by {@link #getRotationMatrix}.
+ * @param I inclination matrix see {@link #getRotationMatrix}.
+ * @return The geomagnetic inclination angle in radians.
+ */
+ public static float getInclination(float[] I) {
+ if (I.length == 9) {
+ return (float)Math.atan2(I[5], I[4]);
+ } else {
+ return (float)Math.atan2(I[6], I[5]);
+ }
+ }
/**
- * Helper function to convert the specified sensor's data to the windows's
- * coordinate space from the device's coordinate space.
- */
-
- private static void mapSensorDataToWindow(int sensor, float[] values, int orientation) {
- final float x = values[DATA_X];
- final float y = values[DATA_Y];
- final float z = values[DATA_Z];
- // copy the raw raw values...
- values[RAW_DATA_X] = x;
- values[RAW_DATA_Y] = y;
- values[RAW_DATA_Z] = z;
- // TODO: add support for 180 and 270 orientations
- if (orientation == Surface.ROTATION_90) {
- switch (sensor) {
- case SENSOR_ACCELEROMETER:
- case SENSOR_MAGNETIC_FIELD:
- values[DATA_X] =-y;
- values[DATA_Y] = x;
- values[DATA_Z] = z;
- break;
- case SENSOR_ORIENTATION:
- case SENSOR_ORIENTATION_RAW:
- values[DATA_X] = x + ((x < 270) ? 90 : -270);
- values[DATA_Y] = z;
- values[DATA_Z] = y;
- break;
+ * Rotates the supplied rotation matrix so it is expressed in a
+ * different coordinate system. This is typically used when an application
+ * needs to compute the three orientation angles of the device (see
+ * {@link #getOrientation}) in a different coordinate system.
+ *
+ * <p>When the rotation matrix is used for drawing (for instance with
+ * OpenGL ES), it usually <b>doesn't need</b> to be transformed by this
+ * function, unless the screen is physically rotated, such as when used
+ * in landscape mode.
+ *
+ * <p><u>Examples:</u><p>
+ *
+ * <li>Using the camera (Y axis along the camera's axis) for an augmented
+ * reality application where the rotation angles are needed :</li><p>
+ *
+ * <code>remapCoordinateSystem(inR, AXIS_X, AXIS_Z, outR);</code><p>
+ *
+ * <li>Using the device as a mechanical compass in landscape mode:</li><p>
+ *
+ * <code>remapCoordinateSystem(inR, AXIS_Y, AXIS_MINUS_X, outR);</code><p>
+ *
+ * Beware of the above example. This call is needed only if the device is
+ * physically used in landscape mode to calculate the rotation angles (see
+ * {@link #getOrientation}).
+ * If the rotation matrix is also used for rendering, it may not need to
+ * be transformed, for instance if your {@link android.app.Activity
+ * Activity} is running in landscape mode.
+ *
+ * <p>Since the resulting coordinate system is orthonormal, only two axes
+ * need to be specified.
+ *
+ * @param inR the rotation matrix to be transformed. Usually it is the
+ * matrix returned by {@link #getRotationMatrix}.
+ * @param X defines on which world axis and direction the X axis of the
+ * device is mapped.
+ * @param Y defines on which world axis and direction the Y axis of the
+ * device is mapped.
+ * @param outR the transformed rotation matrix. inR and outR can be the same
+ * array, but it is not recommended for performance reason.
+ * @return true on success. false if the input parameters are incorrect, for
+ * instance if X and Y define the same axis. Or if inR and outR don't have
+ * the same length.
+ */
+
+ public static boolean remapCoordinateSystem(float[] inR, int X, int Y,
+ float[] outR)
+ {
+ if (inR == outR) {
+ final float[] temp = mTempMatrix;
+ synchronized(temp) {
+ // we don't expect to have a lot of contention
+ if (remapCoordinateSystemImpl(inR, X, Y, temp)) {
+ final int size = outR.length;
+ for (int i=0 ; i<size ; i++)
+ outR[i] = temp[i];
+ return true;
+ }
}
}
+ return remapCoordinateSystemImpl(inR, X, Y, outR);
}
+ private static boolean remapCoordinateSystemImpl(float[] inR, int X, int Y,
+ float[] outR)
+ {
+ /*
+ * X and Y define a rotation matrix 'r':
+ *
+ * (X==1)?((X&0x80)?-1:1):0 (X==2)?((X&0x80)?-1:1):0 (X==3)?((X&0x80)?-1:1):0
+ * (Y==1)?((Y&0x80)?-1:1):0 (Y==2)?((Y&0x80)?-1:1):0 (Y==3)?((X&0x80)?-1:1):0
+ * r[0] ^ r[1]
+ *
+ * where the 3rd line is the vector product of the first 2 lines
+ *
+ */
+
+ final int length = outR.length;
+ if (inR.length != length)
+ return false; // invalid parameter
+ if ((X & 0x7C)!=0 || (Y & 0x7C)!=0)
+ return false; // invalid parameter
+ if (((X & 0x3)==0) || ((Y & 0x3)==0))
+ return false; // no axis specified
+ if ((X & 0x3) == (Y & 0x3))
+ return false; // same axis specified
+
+ // Z is "the other" axis, its sign is either +/- sign(X)*sign(Y)
+ // this can be calculated by exclusive-or'ing X and Y; except for
+ // the sign inversion (+/-) which is calculated below.
+ int Z = X ^ Y;
+
+ // extract the axis (remove the sign), offset in the range 0 to 2.
+ final int x = (X & 0x3)-1;
+ final int y = (Y & 0x3)-1;
+ final int z = (Z & 0x3)-1;
+
+ // compute the sign of Z (whether it needs to be inverted)
+ final int axis_y = (z+1)%3;
+ final int axis_z = (z+2)%3;
+ if (((x^axis_y)|(y^axis_z)) != 0)
+ Z ^= 0x80;
+
+ final boolean sx = (X>=0x80);
+ final boolean sy = (Y>=0x80);
+ final boolean sz = (Z>=0x80);
+
+ // Perform R * r, in avoiding actual muls and adds.
+ final int rowLength = ((length==16)?4:3);
+ for (int j=0 ; j<3 ; j++) {
+ final int offset = j*rowLength;
+ for (int i=0 ; i<3 ; i++) {
+ if (x==i) outR[offset+i] = sx ? -inR[offset+0] : inR[offset+0];
+ if (y==i) outR[offset+i] = sy ? -inR[offset+1] : inR[offset+1];
+ if (z==i) outR[offset+i] = sz ? -inR[offset+2] : inR[offset+2];
+ }
+ }
+ if (length == 16) {
+ outR[3] = outR[7] = outR[11] = outR[12] = outR[13] = outR[14] = 0;
+ outR[15] = 1;
+ }
+ return true;
+ }
- private static native int _sensors_data_open(FileDescriptor fd);
- private static native int _sensors_data_close();
- // returns the sensor's status in the top 4 bits of "res".
- private static native int _sensors_data_poll(float[] values, int sensors);
- private static native int _sensors_data_get_sensors();
+ /**
+ * Computes the device's orientation based on the rotation matrix.
+ * <p> When it returns, the array values is filled with the result:
+ * <li>values[0]: <i>azimuth</i>, rotation around the Z axis.</li>
+ * <li>values[1]: <i>pitch</i>, rotation around the X axis.</li>
+ * <li>values[2]: <i>roll</i>, rotation around the Y axis.</li>
+ * <p>
+ *
+ * @param R rotation matrix see {@link #getRotationMatrix}.
+ * @param values an array of 3 floats to hold the result.
+ * @return The array values passed as argument.
+ */
+ public static float[] getOrientation(float[] R, float values[]) {
+ /*
+ * 4x4 (length=16) case:
+ * / R[ 0] R[ 1] R[ 2] 0 \
+ * | R[ 4] R[ 5] R[ 6] 0 |
+ * | R[ 8] R[ 9] R[10] 0 |
+ * \ 0 0 0 1 /
+ *
+ * 3x3 (length=9) case:
+ * / R[ 0] R[ 1] R[ 2] \
+ * | R[ 3] R[ 4] R[ 5] |
+ * \ R[ 6] R[ 7] R[ 8] /
+ *
+ */
+ if (R.length == 9) {
+ values[0] = (float)Math.atan2(R[1], R[4]);
+ values[1] = (float)Math.asin(-R[7]);
+ values[2] = (float)Math.atan2(-R[6], R[8]);
+ } else {
+ values[0] = (float)Math.atan2(R[1], R[5]);
+ values[1] = (float)Math.asin(-R[9]);
+ values[2] = (float)Math.atan2(-R[8], R[10]);
+ }
+ return values;
+ }
- /** {@hide} */
+
+ /**
+ * {@hide}
+ */
public void onRotationChanged(int rotation) {
synchronized(sListeners) {
sRotation = rotation;
}
}
-
- private static int getRotation() {
+
+ static int getRotation() {
synchronized(sListeners) {
return sRotation;
}
}
-}
+ private class LegacyListener implements SensorEventListener {
+ private float mValues[] = new float[6];
+ @SuppressWarnings("deprecation")
+ private SensorListener mTarget;
+ private int mSensors;
+ private final LmsFilter mYawfilter = new LmsFilter();
+
+ @SuppressWarnings("deprecation")
+ LegacyListener(SensorListener target) {
+ mTarget = target;
+ mSensors = 0;
+ }
+
+ void registerSensor(int legacyType) {
+ mSensors |= legacyType;
+ }
+
+ boolean unregisterSensor(int legacyType) {
+ mSensors &= ~legacyType;
+ int mask = SENSOR_ORIENTATION|SENSOR_ORIENTATION_RAW;
+ if (((legacyType&mask)!=0) && ((mSensors&mask)!=0)) {
+ return false;
+ }
+ return true;
+ }
+
+ @SuppressWarnings("deprecation")
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ try {
+ mTarget.onAccuracyChanged(sensor.getLegacyType(), accuracy);
+ } catch (AbstractMethodError e) {
+ // old app that doesn't implement this method
+ // just ignore it.
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void onSensorChanged(SensorEvent event) {
+ final float v[] = mValues;
+ v[0] = event.values[0];
+ v[1] = event.values[1];
+ v[2] = event.values[2];
+ int legacyType = event.sensor.getLegacyType();
+ mapSensorDataToWindow(legacyType, v, SensorManager.getRotation());
+ if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) {
+ if ((mSensors & SENSOR_ORIENTATION_RAW)!=0) {
+ mTarget.onSensorChanged(SENSOR_ORIENTATION_RAW, v);
+ }
+ if ((mSensors & SENSOR_ORIENTATION)!=0) {
+ v[0] = mYawfilter.filter(event.timestamp, v[0]);
+ mTarget.onSensorChanged(SENSOR_ORIENTATION, v);
+ }
+ } else {
+ mTarget.onSensorChanged(legacyType, v);
+ }
+ }
+
+ /*
+ * Helper function to convert the specified sensor's data to the windows's
+ * coordinate space from the device's coordinate space.
+ *
+ * output: 3,4,5: values in the old API format
+ * 0,1,2: transformed values in the old API format
+ *
+ */
+ private void mapSensorDataToWindow(int sensor,
+ float[] values, int orientation) {
+ float x = values[0];
+ float y = values[1];
+ float z = values[2];
+
+ switch (sensor) {
+ case SensorManager.SENSOR_ORIENTATION:
+ case SensorManager.SENSOR_ORIENTATION_RAW:
+ z = -z;
+ break;
+ case SensorManager.SENSOR_ACCELEROMETER:
+ x = -x;
+ y = -y;
+ z = -z;
+ break;
+ case SensorManager.SENSOR_MAGNETIC_FIELD:
+ x = -x;
+ y = -y;
+ break;
+ }
+ values[0] = x;
+ values[1] = y;
+ values[2] = z;
+ values[3] = x;
+ values[4] = y;
+ values[5] = z;
+ // TODO: add support for 180 and 270 orientations
+ if (orientation == Surface.ROTATION_90) {
+ switch (sensor) {
+ case SENSOR_ACCELEROMETER:
+ case SENSOR_MAGNETIC_FIELD:
+ values[0] =-y;
+ values[1] = x;
+ values[2] = z;
+ break;
+ case SENSOR_ORIENTATION:
+ case SENSOR_ORIENTATION_RAW:
+ values[0] = x + ((x < 270) ? 90 : -270);
+ values[1] = z;
+ values[2] = y;
+ break;
+ }
+ }
+ }
+ }
+
+ class LmsFilter {
+ private static final int SENSORS_RATE_MS = 20;
+ private static final int COUNT = 12;
+ private static final float PREDICTION_RATIO = 1.0f/3.0f;
+ private static final float PREDICTION_TIME = (SENSORS_RATE_MS*COUNT/1000.0f)*PREDICTION_RATIO;
+ private float mV[] = new float[COUNT*2];
+ private float mT[] = new float[COUNT*2];
+ private int mIndex;
+
+ public LmsFilter() {
+ mIndex = COUNT;
+ }
+
+ public float filter(long time, float in) {
+ float v = in;
+ final float ns = 1.0f / 1000000000.0f;
+ final float t = time*ns;
+ float v1 = mV[mIndex];
+ if ((v-v1) > 180) {
+ v -= 360;
+ } else if ((v1-v) > 180) {
+ v += 360;
+ }
+ /* Manage the circular buffer, we write the data twice spaced
+ * by COUNT values, so that we don't have to copy the array
+ * when it's full
+ */
+ mIndex++;
+ if (mIndex >= COUNT*2)
+ mIndex = COUNT;
+ mV[mIndex] = v;
+ mT[mIndex] = t;
+ mV[mIndex-COUNT] = v;
+ mT[mIndex-COUNT] = t;
+
+ float A, B, C, D, E;
+ float a, b;
+ int i;
+
+ A = B = C = D = E = 0;
+ for (i=0 ; i<COUNT-1 ; i++) {
+ final int j = mIndex - 1 - i;
+ final float Z = mV[j];
+ final float T = 0.5f*(mT[j] + mT[j+1]) - t;
+ float dT = mT[j] - mT[j+1];
+ dT *= dT;
+ A += Z*dT;
+ B += T*(T*dT);
+ C += (T*dT);
+ D += Z*(T*dT);
+ E += dT;
+ }
+ b = (A*B + C*D) / (E*B + C*C);
+ a = (E*b - A) / C;
+ float f = b + PREDICTION_TIME*a;
+
+ // Normalize
+ f *= (1.0f / 360.0f);
+ if (((f>=0)?f:-f) >= 0.5f)
+ f = f - (float)Math.ceil(f + 0.5f) + 1.0f;
+ if (f < 0)
+ f += 1.0f;
+ f *= 360.0f;
+ return f;
+ }
+ }
+
+
+ private static native void nativeClassInit();
+
+ private static native int sensors_module_init();
+ private static native int sensors_module_get_next_sensor(Sensor sensor, int next);
+
+ // Used within this module from outside SensorManager, don't make private
+ static native int sensors_data_init();
+ static native int sensors_data_uninit();
+ static native int sensors_data_open(FileDescriptor fd);
+ static native int sensors_data_close();
+ static native int sensors_data_poll(float[] values, int[] status, long[] timestamp);
+}