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