blob: 9b88fff0976f9087b0ce3c50b5a01123f7d284b8 [file] [log] [blame]
/*
* 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;
import android.content.Context;
import android.os.Binder;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.Handler;
import android.os.Message;
import android.os.ServiceManager;
import android.util.Log;
import android.view.IRotationWatcher;
import android.view.IWindowManager;
import android.view.Surface;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Class that lets you access the device's sensors. Get an instance of this
* class by calling {@link android.content.Context#getSystemService(java.lang.String)
* Context.getSystemService()} with an argument of {@link android.content.Context#SENSOR_SERVICE}.
*/
public class SensorManager extends IRotationWatcher.Stub
{
private static final String TAG = "SensorManager";
/** 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.
*/
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)
*
*/
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.
*/
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)
*
*/
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.
*
*/
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)
*/
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.
*/
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.
*/
public static final int SENSOR_ORIENTATION_RAW = 1 << 7;
/** A constant that includes all sensors */
public static final int SENSOR_ALL = 0x7F;
/** Smallest sensor ID */
public static final int SENSOR_MIN = SENSOR_ORIENTATION;
/** Largest sensor ID */
public static final int SENSOR_MAX = ((SENSOR_ALL + 1)>>1);
/** Index of the X value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged} */
public static final int DATA_X = 0;
/** Index of the Y value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged} */
public static final int DATA_Y = 1;
/** Index of the Z value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged} */
public static final int DATA_Z = 2;
/** Offset to the raw values in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged} */
public static final int RAW_DATA_INDEX = 3;
/** Index of the raw X value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged} */
public static final int RAW_DATA_X = 3;
/** Index of the raw X value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged} */
public static final int RAW_DATA_Y = 4;
/** Index of the raw X value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged} */
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;
/** values returned by the accelerometer in various locations in the universe.
* all values are in SI units (m/s^2) */
public static final float GRAVITY_SUN = 275.0f;
public static final float GRAVITY_MERCURY = 3.70f;
public static final float GRAVITY_VENUS = 8.87f;
public static final float GRAVITY_EARTH = 9.80665f;
public static final float GRAVITY_MOON = 1.6f;
public static final float GRAVITY_MARS = 3.71f;
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_PLUTO = 0.6f;
public static final float GRAVITY_DEATH_STAR_I = 0.000000353036145f;
public static final float GRAVITY_THE_ISLAND = 4.815162342f;
/** Maximum magnetic field on Earth's surface */
public static final float MAGNETIC_FIELD_EARTH_MAX = 60.0f;
/** Minimum magnetic field on Earth's surface */
public static final float MAGNETIC_FIELD_EARTH_MIN = 30.0f;
/** Various luminance values during the day (lux) */
public static final float LIGHT_SUNLIGHT_MAX = 120000.0f;
public static final float LIGHT_SUNLIGHT = 110000.0f;
public static final float LIGHT_SHADE = 20000.0f;
public static final float LIGHT_OVERCAST = 10000.0f;
public static final float LIGHT_SUNRISE = 400.0f;
public static final float LIGHT_CLOUDY = 100.0f;
/** Various luminance values during the night (lux) */
public static final float LIGHT_FULLMOON = 0.25f;
public static final float LIGHT_NO_MOON = 0.001f;
/** get sensor data as fast as possible */
public static final int SENSOR_DELAY_FASTEST = 0;
/** rate suitable for games */
public static final int SENSOR_DELAY_GAME = 1;
/** 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;
/** 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,
* 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;
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;
private static IWindowManager sWindowManager;
private static int sRotation = 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 =
new ArrayList<ListenerDelegate>();
static private class SensorThread {
private Thread mThread;
// 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),
SensorThread.class.getName());
mThread.start();
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in startLocked: ", e);
}
}
private class SensorThreadRunnable implements Runnable {
private ISensorService mSensorService;
private ParcelFileDescriptor mSensorDataFd;
private final byte mAccuracies[] = new byte[32];
SensorThreadRunnable(ParcelFileDescriptor fd, ISensorService service) {
mSensorDataFd = fd;
mSensorService = service;
Arrays.fill(mAccuracies, (byte)-1);
}
public void run() {
int sensors_of_interest;
float[] values = new float[6];
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;
}
}
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;
}
}
synchronized (sListeners) {
if (sListeners.isEmpty()) {
// we have no more listeners, terminate the thread
_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);
}
}
}
}
}
}
}
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];
}
int addSensors(int sensors) {
mSensors |= sensors;
return mSensors;
}
int removeSensors(int sensors) {
mSensors &= ~sensors;
return mSensors;
}
boolean hasSensor(int sensor) {
return ((mSensors & sensor) != 0);
}
void onSensorChanged(int sensor, float[] values, int accuracy) {
float[] v;
synchronized (this) {
// remove the array from the pool
v = mValuesPool;
mValuesPool = null;
}
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();
}
Message msg = Message.obtain();
msg.what = sensor;
msg.obj = v;
msg.arg1 = accuracy;
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;
}
}
}
};
}
/**
* {@hide}
*/
public SensorManager(Looper mainLooper) {
mSensorService = ISensorService.Stub.asInterface(
ServiceManager.getService(Context.SENSOR_SERVICE));
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 {
sWindowManager.watchRotation(this);
} catch (RemoteException e) {
}
}
mLooper = mainLooper;
}
/** @return available sensors */
public int getSensors() {
return _sensors_data_get_sensors();
}
/**
* Registers a listener for given sensors.
*
* @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
*/
public boolean registerListener(SensorListener listener, int sensors) {
return registerListener(listener, sensors, SENSOR_DELAY_NORMAL);
}
/**
* Registers a listener for given sensors.
*
* @param listener sensor listener object
* @param sensors a bit masks of the sensors to register to
* @param rate rate of events. 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(SensorListener listener, int sensors, int rate) {
boolean result;
int delay = -1;
switch (rate) {
case SENSOR_DELAY_FASTEST:
delay = 0;
break;
case SENSOR_DELAY_GAME:
delay = 20;
break;
case SENSOR_DELAY_UI:
delay = 60;
break;
case SENSOR_DELAY_NORMAL:
delay = 200;
break;
default:
return false;
}
try {
synchronized (sListeners) {
ListenerDelegate l = null;
for (ListenerDelegate i : sListeners) {
if (i.mListener == listener) {
l = i;
break;
}
}
if (l == null) {
l = new ListenerDelegate(listener, sensors);
result = mSensorService.enableSensor(l, sensors, delay);
if (result) {
sListeners.add(l);
sListeners.notify();
}
if (!sListeners.isEmpty()) {
sSensorThread.startLocked(mSensorService);
}
} else {
result = mSensorService.enableSensor(l, sensors, delay);
if (result) {
l.addSensors(sensors);
}
}
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in registerListener: ", e);
result = false;
}
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) {
try {
synchronized (sListeners) {
final int size = sListeners.size();
for (int i=0 ; i<size ; i++) {
ListenerDelegate l = sListeners.get(i);
if (l.mListener == listener) {
// disable these sensors
mSensorService.enableSensor(l, sensors, SENSOR_DISABLE);
// if we have no more sensors enabled on this listener,
// take it off the list.
if (l.removeSensors(sensors) == 0)
sListeners.remove(i);
break;
}
}
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in unregisterListener: ", e);
}
}
/**
* Unregisters a listener for all sensors.
*
* @param listener a SensorListener object
*/
public void unregisterListener(SensorListener listener) {
unregisterListener(listener, SENSOR_ALL);
}
/**
* 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;
}
}
}
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();
/** {@hide} */
public void onRotationChanged(int rotation) {
synchronized(sListeners) {
sRotation = rotation;
}
}
private static int getRotation() {
synchronized(sListeners) {
return sRotation;
}
}
}