blob: 95916310f9b8718bbfe754dbdc56f547dce0e3da [file] [log] [blame]
/*
* Copyright (C) 2012 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 java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import dalvik.system.CloseGuard;
import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
/**
* Sensor manager implementation that communicates with the built-in
* system sensors.
*
* @hide
*/
public class SystemSensorManager extends SensorManager {
private static native void nativeClassInit();
private static native int nativeGetNextSensor(Sensor sensor, int next);
private static boolean sSensorModuleInitialized = false;
private static final Object sSensorModuleLock = new Object();
private static final ArrayList<Sensor> sFullSensorsList = new ArrayList<Sensor>();
private static final SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>();
// Listener list
private final ArrayList<SensorEventListenerSensorPair> mListenerDelegates = new ArrayList<SensorEventListenerSensorPair>();
// Common pool of sensor events.
private static SensorEventPool sPool;
// Looper associated with the context in which this instance was created.
private final Looper mMainLooper;
// maps a SensorEventListener to a SensorEventQueue
private final Hashtable<SensorEventListener, SensorEventQueue> mSensorEventQueueMap;
/** {@hide} */
public SystemSensorManager(Looper mainLooper) {
mMainLooper = mainLooper;
mSensorEventQueueMap = new Hashtable<SensorEventListener, SensorEventQueue>();
synchronized(sSensorModuleLock) {
if (!sSensorModuleInitialized) {
sSensorModuleInitialized = true;
nativeClassInit();
// initialize the sensor list
final ArrayList<Sensor> fullList = sFullSensorsList;
int i = 0;
do {
Sensor sensor = new Sensor();
i = nativeGetNextSensor(sensor, i);
if (i>=0) {
//Log.d(TAG, "found sensor: " + sensor.getName() +
// ", handle=" + sensor.getHandle());
fullList.add(sensor);
sHandleToSensor.append(sensor.getHandle(), sensor);
}
} while (i>0);
sPool = new SensorEventPool( sFullSensorsList.size()*2 );
}
}
}
/** @hide */
@Override
protected List<Sensor> getFullSensorList() {
return sFullSensorsList;
}
/** @hide */
@Override
protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
int delay, Handler handler)
{
// Invariants to preserve:
// - one Looper per SensorEventListener
// - one Looper per SensorEventQueue
// We map SensorEventListeners to a SensorEventQueue, which holds the looper
if (sensor == null) throw new NullPointerException("sensor cannot be null");
boolean result;
synchronized (mSensorEventQueueMap) {
// check if we already have this SensorEventListener, Sensor pair
// registered -- if so, we ignore the register. This is not ideal
// but this is what the implementation has always been doing.
for (SensorEventListenerSensorPair l : mListenerDelegates) {
if (l.isSameListenerSensorPair(listener, sensor)) {
// already added, just return silently.
return true;
}
}
// now find the SensorEventQueue associated to this listener
SensorEventQueue queue = mSensorEventQueueMap.get(listener);
if (queue != null) {
result = queue.addSensor(sensor, delay);
if (result) {
// create a new ListenerDelegate for this pair
mListenerDelegates.add(new SensorEventListenerSensorPair(listener, sensor));
}
} else {
Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
queue = new SensorEventQueue(listener, looper.getQueue());
result = queue.addSensor(sensor, delay);
if (result) {
// create a new ListenerDelegate for this pair
mListenerDelegates.add(new SensorEventListenerSensorPair(listener, sensor));
mSensorEventQueueMap.put(listener, queue);
} else {
queue.dispose();
}
}
}
return result;
}
/** @hide */
@Override
protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
synchronized (mSensorEventQueueMap) {
// remove this listener/sensor from our list
final ArrayList<SensorEventListenerSensorPair> copy =
new ArrayList<SensorEventListenerSensorPair>(mListenerDelegates);
int lastIndex = copy.size()-1;
for (int i=lastIndex ; i>= 0 ; i--) {
if (copy.get(i).isSameListenerSensorPair(listener, sensor)) {
mListenerDelegates.remove(i);
}
}
// find the SensorEventQueue associated to this SensorEventListener
SensorEventQueue queue = mSensorEventQueueMap.get(listener);
if (queue != null) {
if (sensor != null) {
queue.removeSensor(sensor);
} else {
queue.removeAllSensors();
}
if (!queue.hasSensors()) {
mSensorEventQueueMap.remove(listener);
queue.dispose();
}
}
}
}
/*
* ListenerDelegate is essentially a SensorEventListener, Sensor pair
* and is associated with a single SensorEventQueue.
*/
private static final class SensorEventListenerSensorPair {
private final SensorEventListener mSensorEventListener;
private final Sensor mSensor;
public SensorEventListenerSensorPair(SensorEventListener listener, Sensor sensor) {
mSensorEventListener = listener;
mSensor = sensor;
}
public boolean isSameListenerSensorPair(SensorEventListener listener, Sensor sensor) {
// if sensor is null, we match only on the listener
if (sensor != null) {
return (listener == mSensorEventListener) &&
(sensor.getHandle() == mSensor.getHandle());
} else {
return (listener == mSensorEventListener);
}
}
}
/*
* SensorEventQueue is the communication channel with the sensor service,
* there is a one-to-one mapping between SensorEventQueue and
* SensorEventListener.
*/
private static final class SensorEventQueue {
private static native int nativeInitSensorEventQueue(SensorEventQueue eventQ, MessageQueue msgQ, float[] scratch);
private static native int nativeEnableSensor(int eventQ, int handle, int us);
private static native int nativeDisableSensor(int eventQ, int handle);
private static native void nativeDestroySensorEventQueue(int eventQ);
private int nSensorEventQueue;
private final SensorEventListener mListener;
private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
private final SparseIntArray mSensorAccuracies = new SparseIntArray();
private final SparseBooleanArray mFirstEvent = new SparseBooleanArray();
private final CloseGuard mCloseGuard = CloseGuard.get();
private final float[] mScratch = new float[16];
public SensorEventQueue(SensorEventListener listener, MessageQueue msgQ) {
nSensorEventQueue = nativeInitSensorEventQueue(this, msgQ, mScratch);
mListener = listener;
mCloseGuard.open("dispose");
}
public void dispose() {
dispose(false);
}
public boolean addSensor(Sensor sensor, int delay) {
if (enableSensor(sensor, delay) == 0) {
mActiveSensors.put(sensor.getHandle(), true);
return true;
}
return false;
}
public void removeAllSensors() {
for (int i=0 ; i<mActiveSensors.size(); i++) {
if (mActiveSensors.valueAt(i) == true) {
int handle = mActiveSensors.keyAt(i);
Sensor sensor = sHandleToSensor.get(handle);
if (sensor != null) {
disableSensor(sensor);
mActiveSensors.put(handle, false);
} else {
// it should never happen -- just ignore.
}
}
}
}
public void removeSensor(Sensor sensor) {
final int handle = sensor.getHandle();
if (mActiveSensors.get(handle)) {
disableSensor(sensor);
mActiveSensors.put(sensor.getHandle(), false);
}
}
public boolean hasSensors() {
// no more sensors are set
return mActiveSensors.indexOfValue(true) >= 0;
}
@Override
protected void finalize() throws Throwable {
try {
dispose(true);
} finally {
super.finalize();
}
}
private void dispose(boolean finalized) {
if (mCloseGuard != null) {
if (finalized) {
mCloseGuard.warnIfOpen();
}
mCloseGuard.close();
}
if (nSensorEventQueue != 0) {
nativeDestroySensorEventQueue(nSensorEventQueue);
nSensorEventQueue = 0;
}
}
private int enableSensor(Sensor sensor, int us) {
if (nSensorEventQueue == 0) throw new NullPointerException();
if (sensor == null) throw new NullPointerException();
return nativeEnableSensor(nSensorEventQueue, sensor.getHandle(), us);
}
private int disableSensor(Sensor sensor) {
if (nSensorEventQueue == 0) throw new NullPointerException();
if (sensor == null) throw new NullPointerException();
return nativeDisableSensor(nSensorEventQueue, sensor.getHandle());
}
// Called from native code.
@SuppressWarnings("unused")
private void dispatchSensorEvent(int handle, float[] values, int inAccuracy, long timestamp) {
// this is always called on the same thread.
final SensorEvent t = sPool.getFromPool();
try {
final Sensor sensor = sHandleToSensor.get(handle);
final SensorEventListener listener = mListener;
// FIXME: handle more than 3 values
System.arraycopy(values, 0, t.values, 0, 3);
t.timestamp = timestamp;
t.accuracy = inAccuracy;
t.sensor = sensor;
switch (t.sensor.getType()) {
// Only report accuracy for sensors that support it.
case Sensor.TYPE_MAGNETIC_FIELD:
case Sensor.TYPE_ORIENTATION:
// call onAccuracyChanged() only if the value changes
final int accuracy = mSensorAccuracies.get(handle);
if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
mSensorAccuracies.put(handle, t.accuracy);
listener.onAccuracyChanged(t.sensor, t.accuracy);
}
break;
default:
// For other sensors, just report the accuracy once
if (mFirstEvent.get(handle) == false) {
mFirstEvent.put(handle, true);
listener.onAccuracyChanged(
t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
}
break;
}
listener.onSensorChanged(t);
} finally {
sPool.returnToPool(t);
}
}
}
/*
* A dumb pool of SensorEvent
*/
private static final class SensorEventPool {
private final int mPoolSize;
private final SensorEvent mPool[];
private int mNumItemsInPool;
private SensorEvent createSensorEvent() {
// maximal size for all legacy events is 3
return new SensorEvent(3);
}
SensorEventPool(int poolSize) {
mPoolSize = poolSize;
mNumItemsInPool = poolSize;
mPool = new SensorEvent[poolSize];
}
SensorEvent getFromPool() {
SensorEvent t = null;
synchronized (this) {
if (mNumItemsInPool > 0) {
// remove the "top" item from the pool
final int index = mPoolSize - mNumItemsInPool;
t = mPool[index];
mPool[index] = null;
mNumItemsInPool--;
}
}
if (t == null) {
// the pool was empty or this item was removed from the pool for
// the first time. In any case, we need to create a new item.
t = createSensorEvent();
}
return t;
}
void returnToPool(SensorEvent t) {
synchronized (this) {
// is there space left in the pool?
if (mNumItemsInPool < mPoolSize) {
// if so, return the item to the pool
mNumItemsInPool++;
final int index = mPoolSize - mNumItemsInPool;
mPool[index] = t;
}
}
}
}
}