SystemSensorManager.java revision 6d0c1d78f121d4f1b72973740e8b120c7def1dc0
1    /*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.hardware;
18
19import android.content.Context;
20import android.os.Handler;
21import android.os.Looper;
22import android.os.MessageQueue;
23import android.util.SparseArray;
24import android.util.SparseBooleanArray;
25import android.util.SparseIntArray;
26import dalvik.system.CloseGuard;
27
28import java.util.ArrayList;
29import java.util.HashMap;
30import java.util.List;
31
32/**
33 * Sensor manager implementation that communicates with the built-in
34 * system sensors.
35 *
36 * @hide
37 */
38public class SystemSensorManager extends SensorManager {
39    private static native void nativeClassInit();
40    private static native int nativeGetNextSensor(Sensor sensor, int next);
41
42    private static boolean sSensorModuleInitialized = false;
43    private static final Object sSensorModuleLock = new Object();
44    private static final ArrayList<Sensor> sFullSensorsList = new ArrayList<Sensor>();
45    private static final SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>();
46
47    // Listener list
48    private final HashMap<SensorEventListener, SensorEventQueue> mSensorListeners =
49            new HashMap<SensorEventListener, SensorEventQueue>();
50    private final HashMap<TriggerEventListener, TriggerEventQueue> mTriggerListeners =
51            new HashMap<TriggerEventListener, TriggerEventQueue>();
52
53    // Looper associated with the context in which this instance was created.
54    private final Looper mMainLooper;
55    private final int mTargetSdkLevel;
56
57    /** {@hide} */
58    public SystemSensorManager(Context context, Looper mainLooper) {
59        mMainLooper = mainLooper;
60        mTargetSdkLevel = context.getApplicationInfo().targetSdkVersion;
61        synchronized(sSensorModuleLock) {
62            if (!sSensorModuleInitialized) {
63                sSensorModuleInitialized = true;
64
65                nativeClassInit();
66
67                // initialize the sensor list
68                final ArrayList<Sensor> fullList = sFullSensorsList;
69                int i = 0;
70                do {
71                    Sensor sensor = new Sensor();
72                    i = nativeGetNextSensor(sensor, i);
73                    if (i>=0) {
74                        //Log.d(TAG, "found sensor: " + sensor.getName() +
75                        //        ", handle=" + sensor.getHandle());
76                        fullList.add(sensor);
77                        sHandleToSensor.append(sensor.getHandle(), sensor);
78                    }
79                } while (i>0);
80            }
81        }
82    }
83
84
85    /** @hide */
86    @Override
87    protected List<Sensor> getFullSensorList() {
88        return sFullSensorsList;
89    }
90
91
92    /** @hide */
93    @Override
94    protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
95            int delay, Handler handler)
96    {
97        // Invariants to preserve:
98        // - one Looper per SensorEventListener
99        // - one Looper per SensorEventQueue
100        // We map SensorEventListener to a SensorEventQueue, which holds the looper
101        if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
102
103        // Trigger Sensors should use the requestTriggerSensor call.
104        if (Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT) return false;
105
106        synchronized (mSensorListeners) {
107            SensorEventQueue queue = mSensorListeners.get(listener);
108            if (queue == null) {
109                Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
110                queue = new SensorEventQueue(listener, looper, this);
111                if (!queue.addSensor(sensor, delay)) {
112                    queue.dispose();
113                    return false;
114                }
115                mSensorListeners.put(listener, queue);
116                return true;
117            } else {
118                return queue.addSensor(sensor, delay);
119            }
120        }
121    }
122
123    /** @hide */
124    @Override
125    protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
126        // Trigger Sensors should use the cancelTriggerSensor call.
127        if (sensor != null && Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT) {
128            return;
129        }
130
131        synchronized (mSensorListeners) {
132            SensorEventQueue queue = mSensorListeners.get(listener);
133            if (queue != null) {
134                boolean result;
135                if (sensor == null) {
136                    result = queue.removeAllSensors();
137                } else {
138                    result = queue.removeSensor(sensor);
139                }
140                if (result && !queue.hasSensors()) {
141                    mSensorListeners.remove(listener);
142                    queue.dispose();
143                }
144            }
145        }
146    }
147
148    /** @hide */
149    @Override
150    protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
151        if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
152
153        if (Sensor.getReportingMode(sensor) != Sensor.REPORTING_MODE_ONE_SHOT) return false;
154
155        synchronized (mTriggerListeners) {
156            TriggerEventQueue queue = mTriggerListeners.get(listener);
157            if (queue == null) {
158                queue = new TriggerEventQueue(listener, mMainLooper, this);
159                if (!queue.addSensor(sensor, 0)) {
160                    queue.dispose();
161                    return false;
162                }
163                mTriggerListeners.put(listener, queue);
164                return true;
165            } else {
166                return queue.addSensor(sensor, 0);
167            }
168        }
169    }
170
171    /** @hide */
172    @Override
173    protected boolean cancelTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
174        if (sensor != null && Sensor.getReportingMode(sensor) != Sensor.REPORTING_MODE_ONE_SHOT) {
175            return false;
176        }
177        synchronized (mTriggerListeners) {
178            TriggerEventQueue queue = mTriggerListeners.get(listener);
179            if (queue != null) {
180                boolean result;
181                if (sensor == null) {
182                    result = queue.removeAllSensors();
183                } else {
184                    result = queue.removeSensor(sensor);
185                }
186                if (result && !queue.hasSensors()) {
187                    mTriggerListeners.remove(listener);
188                    queue.dispose();
189                }
190                return result;
191            }
192            return false;
193        }
194    }
195
196    /*
197     * BaseEventQueue is the communication channel with the sensor service,
198     * SensorEventQueue, TriggerEventQueue are subclases and there is one-to-one mapping between
199     * the queues and the listeners.
200     */
201    private static abstract class BaseEventQueue {
202        private native int nativeInitBaseEventQueue(BaseEventQueue eventQ, MessageQueue msgQ,
203
204                float[] scratch);
205        private static native int nativeEnableSensor(int eventQ, int handle, int us);
206        private static native int nativeDisableSensor(int eventQ, int handle);
207        private static native void nativeDestroySensorEventQueue(int eventQ);
208        private int nSensorEventQueue;
209        private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
210        protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
211        protected final SparseBooleanArray mFirstEvent = new SparseBooleanArray();
212        private final CloseGuard mCloseGuard = CloseGuard.get();
213        private final float[] mScratch = new float[16];
214        protected final SystemSensorManager mManager;
215
216        BaseEventQueue(Looper looper, SystemSensorManager manager) {
217            nSensorEventQueue = nativeInitBaseEventQueue(this, looper.getQueue(), mScratch);
218            mCloseGuard.open("dispose");
219            mManager = manager;
220        }
221
222        public void dispose() {
223            dispose(false);
224        }
225
226        public boolean addSensor(Sensor sensor, int delay) {
227            // Check if already present.
228            if (mActiveSensors.get(sensor.getHandle())) return false;
229
230            if (enableSensor(sensor, delay) == 0) {
231                mActiveSensors.put(sensor.getHandle(), true);
232                addSensorEvent(sensor);
233                return true;
234            }
235            return false;
236        }
237
238        public boolean removeAllSensors() {
239            for (int i=0 ; i<mActiveSensors.size(); i++) {
240                if (mActiveSensors.valueAt(i) == true) {
241                    int handle = mActiveSensors.keyAt(i);
242                    Sensor sensor = sHandleToSensor.get(handle);
243                    if (sensor != null) {
244                        disableSensor(sensor);
245                        mActiveSensors.put(handle, false);
246                        removeSensorEvent(sensor);
247                    } else {
248                        // it should never happen -- just ignore.
249                    }
250                }
251            }
252            return true;
253        }
254
255        public boolean removeSensor(Sensor sensor) {
256            final int handle = sensor.getHandle();
257            if (mActiveSensors.get(handle)) {
258                disableSensor(sensor);
259                mActiveSensors.put(sensor.getHandle(), false);
260                removeSensorEvent(sensor);
261                return true;
262            }
263            return false;
264        }
265
266        public boolean hasSensors() {
267            // no more sensors are set
268            return mActiveSensors.indexOfValue(true) >= 0;
269        }
270
271        @Override
272        protected void finalize() throws Throwable {
273            try {
274                dispose(true);
275            } finally {
276                super.finalize();
277            }
278        }
279
280        private void dispose(boolean finalized) {
281            if (mCloseGuard != null) {
282                if (finalized) {
283                    mCloseGuard.warnIfOpen();
284                }
285                mCloseGuard.close();
286            }
287            if (nSensorEventQueue != 0) {
288                nativeDestroySensorEventQueue(nSensorEventQueue);
289                nSensorEventQueue = 0;
290            }
291        }
292
293        private int enableSensor(Sensor sensor, int us) {
294            if (nSensorEventQueue == 0) throw new NullPointerException();
295            if (sensor == null) throw new NullPointerException();
296            return nativeEnableSensor(nSensorEventQueue, sensor.getHandle(), us);
297        }
298        private int disableSensor(Sensor sensor) {
299            if (nSensorEventQueue == 0) throw new NullPointerException();
300            if (sensor == null) throw new NullPointerException();
301            return nativeDisableSensor(nSensorEventQueue, sensor.getHandle());
302        }
303        protected abstract void dispatchSensorEvent(int handle, float[] values, int accuracy,
304                long timestamp);
305
306        protected abstract void addSensorEvent(Sensor sensor);
307        protected abstract void removeSensorEvent(Sensor sensor);
308    }
309
310    static final class SensorEventQueue extends BaseEventQueue {
311        private final SensorEventListener mListener;
312        private final SparseArray<SensorEvent> mSensorsEvents = new SparseArray<SensorEvent>();
313
314        public SensorEventQueue(SensorEventListener listener, Looper looper,
315                SystemSensorManager manager) {
316            super(looper, manager);
317            mListener = listener;
318        }
319
320        public void addSensorEvent(Sensor sensor) {
321            SensorEvent t = new SensorEvent(Sensor.getMaxLengthValuesArray(sensor,
322                    mManager.mTargetSdkLevel));
323            mSensorsEvents.put(sensor.getHandle(), t);
324        }
325
326        public void removeSensorEvent(Sensor sensor) {
327            mSensorsEvents.delete(sensor.getHandle());
328        }
329
330        // Called from native code.
331        @SuppressWarnings("unused")
332        @Override
333        protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy,
334                long timestamp) {
335            final Sensor sensor = sHandleToSensor.get(handle);
336            SensorEvent t = mSensorsEvents.get(handle);
337            // Copy from the values array.
338            System.arraycopy(values, 0, t.values, 0, t.values.length);
339            t.timestamp = timestamp;
340            t.accuracy = inAccuracy;
341            t.sensor = sensor;
342            switch (t.sensor.getType()) {
343                // Only report accuracy for sensors that support it.
344                case Sensor.TYPE_MAGNETIC_FIELD:
345                case Sensor.TYPE_ORIENTATION:
346                    // call onAccuracyChanged() only if the value changes
347                    final int accuracy = mSensorAccuracies.get(handle);
348                    if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
349                        mSensorAccuracies.put(handle, t.accuracy);
350                        mListener.onAccuracyChanged(t.sensor, t.accuracy);
351                    }
352                    break;
353                default:
354                    // For other sensors, just report the accuracy once
355                    if (mFirstEvent.get(handle) == false) {
356                        mFirstEvent.put(handle, true);
357                        mListener.onAccuracyChanged(
358                                t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
359                    }
360                    break;
361            }
362            mListener.onSensorChanged(t);
363        }
364    }
365
366    static final class TriggerEventQueue extends BaseEventQueue {
367        private final TriggerEventListener mListener;
368        private final SparseArray<TriggerEvent> mTriggerEvents = new SparseArray<TriggerEvent>();
369
370        public TriggerEventQueue(TriggerEventListener listener, Looper looper,
371                SystemSensorManager manager) {
372            super(looper, manager);
373            mListener = listener;
374        }
375
376        public void addSensorEvent(Sensor sensor) {
377            TriggerEvent t = new TriggerEvent(Sensor.getMaxLengthValuesArray(sensor,
378                    mManager.mTargetSdkLevel));
379            mTriggerEvents.put(sensor.getHandle(), t);
380        }
381
382        public void removeSensorEvent(Sensor sensor) {
383            mTriggerEvents.delete(sensor.getHandle());
384        }
385
386        // Called from native code.
387        @SuppressWarnings("unused")
388        @Override
389        protected void dispatchSensorEvent(int handle, float[] values, int accuracy,
390                long timestamp) {
391            final Sensor sensor = sHandleToSensor.get(handle);
392            TriggerEvent t = mTriggerEvents.get(handle);
393
394            // Copy from the values array.
395            System.arraycopy(values, 0, t.values, 0, t.values.length);
396            t.timestamp = timestamp;
397            t.sensor = sensor;
398
399            // A trigger sensor should be auto disabled.
400            mManager.cancelTriggerSensorImpl(mListener, sensor);
401
402            mListener.onTrigger(t);
403        }
404    }
405}
406