| /* |
| * 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 android.content.Context; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.MessageQueue; |
| import android.util.Log; |
| import android.util.SparseArray; |
| import android.util.SparseBooleanArray; |
| import android.util.SparseIntArray; |
| import dalvik.system.CloseGuard; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| |
| /** |
| * Sensor manager implementation that communicates with the built-in |
| * 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 HashMap<SensorEventListener, SensorEventQueue> mSensorListeners = |
| new HashMap<SensorEventListener, SensorEventQueue>(); |
| private final HashMap<TriggerEventListener, TriggerEventQueue> mTriggerListeners = |
| new HashMap<TriggerEventListener, TriggerEventQueue>(); |
| |
| // Looper associated with the context in which this instance was created. |
| private final Looper mMainLooper; |
| private final int mTargetSdkLevel; |
| |
| /** {@hide} */ |
| public SystemSensorManager(Context context, Looper mainLooper) { |
| mMainLooper = mainLooper; |
| mTargetSdkLevel = context.getApplicationInfo().targetSdkVersion; |
| 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); |
| } |
| } |
| } |
| |
| |
| /** @hide */ |
| @Override |
| protected List<Sensor> getFullSensorList() { |
| return sFullSensorsList; |
| } |
| |
| |
| /** @hide */ |
| @Override |
| protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor, |
| int delayUs, Handler handler, int maxBatchReportLatencyUs, int reservedFlags) { |
| if (listener == null || sensor == null) { |
| Log.e(TAG, "sensor or listener is null"); |
| return false; |
| } |
| // Trigger Sensors should use the requestTriggerSensor call. |
| if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) { |
| Log.e(TAG, "Trigger Sensors should use the requestTriggerSensor."); |
| return false; |
| } |
| if (maxBatchReportLatencyUs < 0 || delayUs < 0) { |
| Log.e(TAG, "maxBatchReportLatencyUs and delayUs should be non-negative"); |
| return false; |
| } |
| |
| // Invariants to preserve: |
| // - one Looper per SensorEventListener |
| // - one Looper per SensorEventQueue |
| // We map SensorEventListener to a SensorEventQueue, which holds the looper |
| synchronized (mSensorListeners) { |
| SensorEventQueue queue = mSensorListeners.get(listener); |
| if (queue == null) { |
| Looper looper = (handler != null) ? handler.getLooper() : mMainLooper; |
| queue = new SensorEventQueue(listener, looper, this); |
| if (!queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs, reservedFlags)) { |
| queue.dispose(); |
| return false; |
| } |
| mSensorListeners.put(listener, queue); |
| return true; |
| } else { |
| return queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs, reservedFlags); |
| } |
| } |
| } |
| |
| /** @hide */ |
| @Override |
| protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) { |
| // Trigger Sensors should use the cancelTriggerSensor call. |
| if (sensor != null && sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) { |
| return; |
| } |
| |
| synchronized (mSensorListeners) { |
| SensorEventQueue queue = mSensorListeners.get(listener); |
| if (queue != null) { |
| boolean result; |
| if (sensor == null) { |
| result = queue.removeAllSensors(); |
| } else { |
| result = queue.removeSensor(sensor, true); |
| } |
| if (result && !queue.hasSensors()) { |
| mSensorListeners.remove(listener); |
| queue.dispose(); |
| } |
| } |
| } |
| } |
| |
| /** @hide */ |
| @Override |
| protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) { |
| if (sensor == null) throw new IllegalArgumentException("sensor cannot be null"); |
| |
| if (sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) return false; |
| |
| synchronized (mTriggerListeners) { |
| TriggerEventQueue queue = mTriggerListeners.get(listener); |
| if (queue == null) { |
| queue = new TriggerEventQueue(listener, mMainLooper, this); |
| if (!queue.addSensor(sensor, 0, 0, 0)) { |
| queue.dispose(); |
| return false; |
| } |
| mTriggerListeners.put(listener, queue); |
| return true; |
| } else { |
| return queue.addSensor(sensor, 0, 0, 0); |
| } |
| } |
| } |
| |
| /** @hide */ |
| @Override |
| protected boolean cancelTriggerSensorImpl(TriggerEventListener listener, Sensor sensor, |
| boolean disable) { |
| if (sensor != null && sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) { |
| return false; |
| } |
| synchronized (mTriggerListeners) { |
| TriggerEventQueue queue = mTriggerListeners.get(listener); |
| if (queue != null) { |
| boolean result; |
| if (sensor == null) { |
| result = queue.removeAllSensors(); |
| } else { |
| result = queue.removeSensor(sensor, disable); |
| } |
| if (result && !queue.hasSensors()) { |
| mTriggerListeners.remove(listener); |
| queue.dispose(); |
| } |
| return result; |
| } |
| return false; |
| } |
| } |
| |
| protected boolean flushImpl(SensorEventListener listener) { |
| if (listener == null) throw new IllegalArgumentException("listener cannot be null"); |
| |
| synchronized (mSensorListeners) { |
| SensorEventQueue queue = mSensorListeners.get(listener); |
| if (queue == null) { |
| return false; |
| } else { |
| return (queue.flush() == 0); |
| } |
| } |
| } |
| |
| /* |
| * BaseEventQueue is the communication channel with the sensor service, |
| * SensorEventQueue, TriggerEventQueue are subclases and there is one-to-one mapping between |
| * the queues and the listeners. |
| */ |
| private static abstract class BaseEventQueue { |
| private native long nativeInitBaseEventQueue(BaseEventQueue eventQ, MessageQueue msgQ, |
| float[] scratch); |
| private static native int nativeEnableSensor(long eventQ, int handle, int rateUs, |
| int maxBatchReportLatencyUs, int reservedFlags); |
| private static native int nativeDisableSensor(long eventQ, int handle); |
| private static native void nativeDestroySensorEventQueue(long eventQ); |
| private static native int nativeFlushSensor(long eventQ); |
| private long nSensorEventQueue; |
| private final SparseBooleanArray mActiveSensors = new SparseBooleanArray(); |
| protected final SparseIntArray mSensorAccuracies = new SparseIntArray(); |
| protected final SparseBooleanArray mFirstEvent = new SparseBooleanArray(); |
| private final CloseGuard mCloseGuard = CloseGuard.get(); |
| private final float[] mScratch = new float[16]; |
| protected final SystemSensorManager mManager; |
| |
| BaseEventQueue(Looper looper, SystemSensorManager manager) { |
| nSensorEventQueue = nativeInitBaseEventQueue(this, looper.getQueue(), mScratch); |
| mCloseGuard.open("dispose"); |
| mManager = manager; |
| } |
| |
| public void dispose() { |
| dispose(false); |
| } |
| |
| public boolean addSensor( |
| Sensor sensor, int delayUs, int maxBatchReportLatencyUs, int reservedFlags) { |
| // Check if already present. |
| int handle = sensor.getHandle(); |
| if (mActiveSensors.get(handle)) return false; |
| |
| // Get ready to receive events before calling enable. |
| mActiveSensors.put(handle, true); |
| addSensorEvent(sensor); |
| if (enableSensor(sensor, delayUs, maxBatchReportLatencyUs, reservedFlags) != 0) { |
| // Try continuous mode if batching fails. |
| if (maxBatchReportLatencyUs == 0 || |
| maxBatchReportLatencyUs > 0 && enableSensor(sensor, delayUs, 0, 0) != 0) { |
| removeSensor(sensor, false); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public boolean 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); |
| removeSensorEvent(sensor); |
| } else { |
| // it should never happen -- just ignore. |
| } |
| } |
| } |
| return true; |
| } |
| |
| public boolean removeSensor(Sensor sensor, boolean disable) { |
| final int handle = sensor.getHandle(); |
| if (mActiveSensors.get(handle)) { |
| if (disable) disableSensor(sensor); |
| mActiveSensors.put(sensor.getHandle(), false); |
| removeSensorEvent(sensor); |
| return true; |
| } |
| return false; |
| } |
| |
| public int flush() { |
| if (nSensorEventQueue == 0) throw new NullPointerException(); |
| return nativeFlushSensor(nSensorEventQueue); |
| } |
| |
| 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 rateUs, int maxBatchReportLatencyUs, int reservedFlags) { |
| if (nSensorEventQueue == 0) throw new NullPointerException(); |
| if (sensor == null) throw new NullPointerException(); |
| return nativeEnableSensor(nSensorEventQueue, sensor.getHandle(), rateUs, |
| maxBatchReportLatencyUs, reservedFlags); |
| } |
| |
| private int disableSensor(Sensor sensor) { |
| if (nSensorEventQueue == 0) throw new NullPointerException(); |
| if (sensor == null) throw new NullPointerException(); |
| return nativeDisableSensor(nSensorEventQueue, sensor.getHandle()); |
| } |
| protected abstract void dispatchSensorEvent(int handle, float[] values, int accuracy, |
| long timestamp); |
| protected abstract void dispatchFlushCompleteEvent(int handle); |
| |
| protected abstract void addSensorEvent(Sensor sensor); |
| protected abstract void removeSensorEvent(Sensor sensor); |
| } |
| |
| static final class SensorEventQueue extends BaseEventQueue { |
| private final SensorEventListener mListener; |
| private final SparseArray<SensorEvent> mSensorsEvents = new SparseArray<SensorEvent>(); |
| |
| public SensorEventQueue(SensorEventListener listener, Looper looper, |
| SystemSensorManager manager) { |
| super(looper, manager); |
| mListener = listener; |
| } |
| |
| @Override |
| public void addSensorEvent(Sensor sensor) { |
| SensorEvent t = new SensorEvent(Sensor.getMaxLengthValuesArray(sensor, |
| mManager.mTargetSdkLevel)); |
| synchronized (mSensorsEvents) { |
| mSensorsEvents.put(sensor.getHandle(), t); |
| } |
| } |
| |
| @Override |
| public void removeSensorEvent(Sensor sensor) { |
| synchronized (mSensorsEvents) { |
| mSensorsEvents.delete(sensor.getHandle()); |
| } |
| } |
| |
| // Called from native code. |
| @SuppressWarnings("unused") |
| @Override |
| protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy, |
| long timestamp) { |
| final Sensor sensor = sHandleToSensor.get(handle); |
| SensorEvent t = null; |
| synchronized (mSensorsEvents) { |
| t = mSensorsEvents.get(handle); |
| } |
| |
| if (t == null) { |
| // This may happen if the client has unregistered and there are pending events in |
| // the queue waiting to be delivered. Ignore. |
| return; |
| } |
| // Copy from the values array. |
| System.arraycopy(values, 0, t.values, 0, t.values.length); |
| t.timestamp = timestamp; |
| t.accuracy = inAccuracy; |
| t.sensor = sensor; |
| |
| // 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); |
| mListener.onAccuracyChanged(t.sensor, t.accuracy); |
| } |
| mListener.onSensorChanged(t); |
| } |
| |
| @SuppressWarnings("unused") |
| protected void dispatchFlushCompleteEvent(int handle) { |
| if (mListener instanceof SensorEventListener2) { |
| final Sensor sensor = sHandleToSensor.get(handle); |
| ((SensorEventListener2)mListener).onFlushCompleted(sensor); |
| } |
| return; |
| } |
| } |
| |
| static final class TriggerEventQueue extends BaseEventQueue { |
| private final TriggerEventListener mListener; |
| private final SparseArray<TriggerEvent> mTriggerEvents = new SparseArray<TriggerEvent>(); |
| |
| public TriggerEventQueue(TriggerEventListener listener, Looper looper, |
| SystemSensorManager manager) { |
| super(looper, manager); |
| mListener = listener; |
| } |
| |
| @Override |
| public void addSensorEvent(Sensor sensor) { |
| TriggerEvent t = new TriggerEvent(Sensor.getMaxLengthValuesArray(sensor, |
| mManager.mTargetSdkLevel)); |
| synchronized (mTriggerEvents) { |
| mTriggerEvents.put(sensor.getHandle(), t); |
| } |
| } |
| |
| @Override |
| public void removeSensorEvent(Sensor sensor) { |
| synchronized (mTriggerEvents) { |
| mTriggerEvents.delete(sensor.getHandle()); |
| } |
| } |
| |
| // Called from native code. |
| @SuppressWarnings("unused") |
| @Override |
| protected void dispatchSensorEvent(int handle, float[] values, int accuracy, |
| long timestamp) { |
| final Sensor sensor = sHandleToSensor.get(handle); |
| TriggerEvent t = null; |
| synchronized (mTriggerEvents) { |
| t = mTriggerEvents.get(handle); |
| } |
| if (t == null) { |
| Log.e(TAG, "Error: Trigger Event is null for Sensor: " + sensor); |
| return; |
| } |
| |
| // Copy from the values array. |
| System.arraycopy(values, 0, t.values, 0, t.values.length); |
| t.timestamp = timestamp; |
| t.sensor = sensor; |
| |
| // A trigger sensor is auto disabled. So just clean up and don't call native |
| // disable. |
| mManager.cancelTriggerSensorImpl(mListener, sensor, false); |
| |
| mListener.onTrigger(t); |
| } |
| |
| @SuppressWarnings("unused") |
| protected void dispatchFlushCompleteEvent(int handle) { |
| } |
| } |
| } |