diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:43 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:43 -0800 |
commit | f013e1afd1e68af5e3b868c26a653bbfb39538f8 (patch) | |
tree | 7ad6c8fd9c7b55f4b4017171dec1cb760bbd26bf /core/java/android/hardware | |
parent | e70cfafe580c6f2994c4827cd8a534aabf3eb05c (diff) | |
download | frameworks_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')
-rw-r--r-- | core/java/android/hardware/Camera.java | 33 | ||||
-rw-r--r-- | core/java/android/hardware/ISensorService.aidl | 1 | ||||
-rw-r--r-- | core/java/android/hardware/Sensor.java | 146 | ||||
-rw-r--r-- | core/java/android/hardware/SensorEvent.java | 146 | ||||
-rw-r--r-- | core/java/android/hardware/SensorEventListener.java | 49 | ||||
-rw-r--r-- | core/java/android/hardware/SensorListener.java | 64 | ||||
-rw-r--r-- | core/java/android/hardware/SensorManager.java | 1366 |
7 files changed, 1523 insertions, 282 deletions
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 8330750..dc75748 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -18,6 +18,7 @@ package android.hardware; import java.lang.ref.WeakReference; import java.util.HashMap; +import java.io.IOException; import android.util.Log; import android.view.Surface; @@ -98,13 +99,28 @@ public class Camera { } /** + * Reconnect to the camera after passing it to MediaRecorder. To save + * setup/teardown time, a client of Camara can pass an initialized Camera + * object to a MediaRecorder to use for video recording. Once the + * MediaRecorder is done with the Camera, this method can be used to + * re-establish a connection with the camera hardware. + * + * @throws IOException if the method fails. + * + * FIXME: Unhide after approval + * @hide + */ + public native final void reconnect() throws IOException; + + /** * Sets the SurfaceHolder to be used for a picture preview. If the surface * changed since the last call, the screen will blank. Nothing happens * if the same surface is re-set. * * @param holder the SurfaceHolder upon which to place the picture preview + * @throws IOException if the method fails. */ - public final void setPreviewDisplay(SurfaceHolder holder) { + public final void setPreviewDisplay(SurfaceHolder holder) throws IOException { setPreviewDisplay(holder.getSurface()); } @@ -263,10 +279,19 @@ public class Camera { }; /** - * Registers a callback to be invoked when a picture is taken. + * Triggers an asynchronous image capture. The camera service + * will initiate a series of callbacks to the application as the + * image capture progresses. The shutter callback occurs after + * the image is captured. This can be used to trigger a sound + * to let the user know that image has been captured. The raw + * callback occurs when the raw image data is available. The jpeg + * callback occurs when the compressed image is available. If the + * application does not need a particular callback, a null can be + * passed instead of a callback method. * - * @param raw the callback to run for raw images, may be null - * @param jpeg the callback to run for jpeg images, may be null + * @param shutter callback after the image is captured, may be null + * @param raw callback with raw image data, may be null + * @param jpeg callback with jpeg image data, may be null */ public final void takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback jpeg) { diff --git a/core/java/android/hardware/ISensorService.aidl b/core/java/android/hardware/ISensorService.aidl index b6ac3ab..8aad9b4 100644 --- a/core/java/android/hardware/ISensorService.aidl +++ b/core/java/android/hardware/ISensorService.aidl @@ -26,5 +26,4 @@ interface ISensorService { ParcelFileDescriptor getDataChanel(); boolean enableSensor(IBinder listener, int sensor, int enable); - oneway void reportAccuracy(int sensor, int value); } diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java new file mode 100644 index 0000000..0ce2f7b --- /dev/null +++ b/core/java/android/hardware/Sensor.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2008 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; + +/** + * Class representing a sensor. Use {@link SensorManager#getSensorList} + * to get the list of available Sensors. + */ +public class Sensor { + + /** + * A constant describing an accelerometer sensor type. + * See {@link android.hardware.SensorEvent SensorEvent} + * for more details. + */ + public static final int TYPE_ACCELEROMETER = 1; + + /** + * A constant describing a magnetic field sensor type. + * See {@link android.hardware.SensorEvent SensorEvent} + * for more details. + */ + public static final int TYPE_MAGNETIC_FIELD = 2; + + /** + * A constant describing an orientation sensor type. + * See {@link android.hardware.SensorEvent SensorEvent} + * for more details. + */ + public static final int TYPE_ORIENTATION = 3; + + /** A constant describing a gyroscope sensor type */ + public static final int TYPE_GYROSCOPE = 4; + /** A constant describing a light sensor type */ + public static final int TYPE_LIGHT = 5; + /** A constant describing a pressure sensor type */ + public static final int TYPE_PRESSURE = 6; + /** A constant describing a temperature sensor type */ + public static final int TYPE_TEMPERATURE = 7; + /** A constant describing a proximity sensor type */ + public static final int TYPE_PROXIMITY = 8; + + + /** + * A constant describing all sensor types. + */ + public static final int TYPE_ALL = -1; + + /* Some of these fields are set only by the native bindings in + * SensorManager. + */ + private String mName; + private String mVendor; + private int mVersion; + private int mHandle; + private int mType; + private float mMaxRange; + private float mResolution; + private float mPower; + private int mLegacyType; + + + Sensor() { + } + + /** + * @return name string of the sensor. + */ + public String getName() { + return mName; + } + + /** + * @return vendor string of this sensor. + */ + public String getVendor() { + return mVendor; + } + + /** + * @return generic type of this sensor. + */ + public int getType() { + return mType; + } + + /** + * @return version of the sensor's module. + */ + public int getVersion() { + return mVersion; + } + + /** + * @return maximum range of the sensor in the sensor's unit. + */ + public float getMaximumRange() { + return mMaxRange; + } + + /** + * @return resolution of the sensor in the sensor's unit. + */ + public float getResolution() { + return mResolution; + } + + /** + * @return the power in mA used by this sensor while in use + */ + public float getPower() { + return mPower; + } + + int getHandle() { + return mHandle; + } + + void setRange(float max, float res) { + mMaxRange = max; + mResolution = res; + } + + void setLegacyType(int legacyType) { + mLegacyType = legacyType; + } + + int getLegacyType() { + return mLegacyType; + } +} diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java new file mode 100644 index 0000000..cf939c5 --- /dev/null +++ b/core/java/android/hardware/SensorEvent.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2008 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 sensor event and holds informations such as the + * sensor type (eg: accelerometer, orientation, etc...), the time-stamp, + * accuracy and of course the sensor's {@link SensorEvent#values data}. + * + * <p><u>Definition of the coordinate system used by the SensorEvent API.</u><p> + * + * <pre> + * The coordinate space is defined relative to the screen of the phone + * in its default orientation. The axes are not swapped when the device's + * screen orientation changes. + * + * The OpenGL ES coordinate system is used. The origin is in the + * lower-left corner with respect to the screen, with the X axis horizontal + * and pointing right, the Y axis vertical and pointing up and the Z axis + * pointing outside the front face of the screen. In this system, coordinates + * behind the screen have negative Z values. + * + * <b>Note:</b> This coordinate system is different from the one used in the + * Android 2D APIs where the origin is in the top-left corner. + * + * x<0 x>0 + * ^ + * | + * +-----------+--> y>0 + * | | + * | | + * | | + * | | / z<0 + * | | / + * | | / + * O-----------+/ + * |[] [ ] []/ + * +----------/+ y<0 + * / + * / + * |/ z>0 (toward the sky) + * + * O: Origin (x=0,y=0,z=0) + * </pre> + */ + +public class SensorEvent { + /** + * The length and contents of the values array vary depending on which + * sensor type is being monitored (see also {@link SensorEvent} for a + * definition of the coordinate system used): + * + * <p>{@link android.hardware.Sensor#TYPE_ORIENTATION Sensor.TYPE_ORIENTATION}:<p> + * All values are angles in degrees. + * + * <p>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>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>values[2]: Roll, rotation around Y axis (-90 to 90), with + * positive values when the x-axis moves <b>away</b> from the z-axis. + * + * <p><b>Note:</b> This definition is different from <b>yaw, pitch and + * roll</b> used in aviation where the X axis is along the long side of + * the plane (tail to nose). + * + * <p><b>Note:</b> It is preferable to use + * {@link android.hardware.SensorManager#getRotationMatrix + * getRotationMatrix()} in conjunction with + * {@link android.hardware.SensorManager#remapCoordinateSystem + * remapCoordinateSystem()} and + * {@link android.hardware.SensorManager#getOrientation getOrientation()} + * to compute these values; while it may be more expensive, it is usually + * more accurate. + * + * <p>{@link android.hardware.Sensor#TYPE_ACCELEROMETER Sensor.TYPE_ACCELEROMETER}:<p> + * All values are in SI units (m/s^2) and measure the acceleration applied + * to the phone minus the force of gravity. + * + * <p>values[0]: Acceleration minus Gx on the x-axis + * <p>values[1]: Acceleration minus Gy on the y-axis + * <p>values[2]: Acceleration minus Gz on the z-axis + * + * <p><u>Examples</u>: + * <li>When the device lies flat on a table and is pushed on its left + * side toward the right, the x acceleration value is positive.</li> + * + * <li>When the device lies flat on a table, the acceleration value is + * +9.81, which correspond to the acceleration of the device (0 m/s^2) + * minus the force of gravity (-9.81 m/s^2).</li> + * + * <li>When the device lies flat on a table and is pushed toward the sky + * with an acceleration of A m/s^2, the acceleration value is equal to + * A+9.81 which correspond to the acceleration of the + * device (+A m/s^2) minus the force of gravity (-9.81 m/s^2).</li> + * + * + * <p>{@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD Sensor.TYPE_MAGNETIC_FIELD}:<p> + * All values are in micro-Tesla (uT) and measure the ambient magnetic + * field in the X, Y and Z axis. + * + */ + public final float[] values; + + /** + * The sensor that generated this event. + * See {@link android.hardware.SensorManager SensorManager} + * for details. + */ + public Sensor sensor; + + /** + * The accuracy of this event. + * See {@link android.hardware.SensorManager SensorManager} + * for details. + */ + public int accuracy; + + + /** + * The time in nanosecond at which the event happened + */ + public long timestamp; + + + SensorEvent(int size) { + values = new float[size]; + } +} diff --git a/core/java/android/hardware/SensorEventListener.java b/core/java/android/hardware/SensorEventListener.java new file mode 100644 index 0000000..716d0d4 --- /dev/null +++ b/core/java/android/hardware/SensorEventListener.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008 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; + +/** + * Used for receiving notifications from the SensorManager when + * sensor values have changed. + */ +public interface SensorEventListener { + + /** + * Called when sensor values have changed. + * <p>See {@link android.hardware.SensorManager SensorManager} + * for details on possible sensor types. + * <p>See also {@link android.hardware.SensorEvent SensorEvent}. + * + * <p><b>NOTE:</b> The application doesn't own the + * {@link android.hardware.SensorEvent event} + * object passed as a parameter and therefore cannot hold on o it. + * The object may be part of an internal pool and may be reused by + * the framework. + * + * @param event the {@link android.hardware.SensorEvent SensorEvent}. + */ + public void onSensorChanged(SensorEvent event); + + /** + * Called when the accuracy of a sensor has changed. + * <p>See {@link android.hardware.SensorManager SensorManager} + * for details. + * + * @param accuracy The new accuracy of this sensor + */ + public void onAccuracyChanged(Sensor sensor, int accuracy); +} diff --git a/core/java/android/hardware/SensorListener.java b/core/java/android/hardware/SensorListener.java index d676a5e..cfa184b 100644 --- a/core/java/android/hardware/SensorListener.java +++ b/core/java/android/hardware/SensorListener.java @@ -19,18 +19,74 @@ package android.hardware; /** * Used for receiving notifications from the SensorManager when * sensor values have changed. + * + * This interface is deprecated, use + * {@link android.hardware.SensorEventListener SensorEventListener} instead. + * */ +@Deprecated public interface SensorListener { /** - * Called when sensor values have changed. + * <p>Called when sensor values have changed. * The length and contents of the values array vary * depending on which sensor is being monitored. * See {@link android.hardware.SensorManager SensorManager} - * for details on possible sensor types and values. + * for details on possible sensor types. * + * <p><u>Definition of the coordinate system used below.</u><p> + * <p>The X axis refers to the screen's horizontal axis + * (the small edge in portrait mode, the long edge in landscape mode) and + * points to the right. + * <p>The Y axis refers to the screen's vertical axis and points towards + * the top of the screen (the origin is in the lower-left corner). + * <p>The Z axis points toward the sky when the device is lying on its back + * on a table. + * <p> <b>IMPORTANT NOTE:</b> The axis <b><u>are swapped</u></b> when the + * device's screen orientation changes. To access the unswapped values, + * use indices 3, 4 and 5 in values[]. + * + * <p>{@link android.hardware.SensorManager#SENSOR_ORIENTATION SENSOR_ORIENTATION}, + * {@link android.hardware.SensorManager#SENSOR_ORIENTATION_RAW SENSOR_ORIENTATION_RAW}:<p> + * All values are angles in degrees. + * + * <p>values[0]: Azimuth, rotation around the Z axis (0<=azimuth<360). + * 0 = North, 90 = East, 180 = South, 270 = West + * + * <p>values[1]: Pitch, rotation around X axis (-180<=pitch<=180), with positive + * values when the z-axis moves toward the y-axis. + * + * <p>values[2]: Roll, rotation around Y axis (-90<=roll<=90), with positive values + * when the z-axis moves toward the x-axis. + * + * <p>Note that this definition of yaw, pitch and roll is different from the + * traditional definition used in aviation where the X axis is along the long + * side of the plane (tail to nose). + * + * <p>{@link android.hardware.SensorManager#SENSOR_ACCELEROMETER SENSOR_ACCELEROMETER}:<p> + * All values are in SI units (m/s^2) and measure contact forces. + * + * <p>values[0]: force applied by the device on the x-axis + * <p>values[1]: force applied by the device on the y-axis + * <p>values[2]: force applied by the device on the z-axis + * + * <p><u>Examples</u>: + * <li>When the device is pushed on its left side toward the right, the + * x acceleration value is negative (the device applies a reaction force + * to the push toward the left)</li> + * + * <li>When the device lies flat on a table, the acceleration value is + * {@link android.hardware.SensorManager#STANDARD_GRAVITY -STANDARD_GRAVITY}, + * which correspond to the force the device applies on the table in reaction + * to gravity.</li> + * + * <p>{@link android.hardware.SensorManager#SENSOR_MAGNETIC_FIELD SENSOR_MAGNETIC_FIELD}:<p> + * All values are in micro-Tesla (uT) and measure the ambient magnetic + * field in the X, Y and -Z axis. + * <p><b><u>Note:</u></b> the magnetic field's Z axis is inverted. + * * @param sensor The ID of the sensor being monitored - * @param values The new values for the sensor + * @param values The new values for the sensor. */ public void onSensorChanged(int sensor, float[] values); @@ -40,7 +96,7 @@ public interface SensorListener { * for details. * * @param sensor The ID of the sensor being monitored - * @param accuracy The new accuracy of this sensor + * @param accuracy The new accuracy of this sensor. */ public void onAccuracyChanged(int sensor, int accuracy); } 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); +} |