SystemSensorManager.java revision 88445990b426499f25a9f37bc3b75f6e0267b4cb
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.Log;
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    // Looper associated with the context in which this instance was created.
55    private final Looper mMainLooper;
56    private final int mTargetSdkLevel;
57    private final String mPackageName;
58
59    /** {@hide} */
60    public SystemSensorManager(Context context, Looper mainLooper) {
61        mMainLooper = mainLooper;
62        mTargetSdkLevel = context.getApplicationInfo().targetSdkVersion;
63        mPackageName = context.getPackageName();
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        }
85    }
86
87
88    /** @hide */
89    @Override
90    protected List<Sensor> getFullSensorList() {
91        return sFullSensorsList;
92    }
93
94
95    /** @hide */
96    @Override
97    protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
98            int delayUs, Handler handler, int maxBatchReportLatencyUs, int reservedFlags) {
99        if (listener == null || sensor == null) {
100            Log.e(TAG, "sensor or listener is null");
101            return false;
102        }
103        // Trigger Sensors should use the requestTriggerSensor call.
104        if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
105            Log.e(TAG, "Trigger Sensors should use the requestTriggerSensor.");
106            return false;
107        }
108        if (maxBatchReportLatencyUs < 0 || delayUs < 0) {
109            Log.e(TAG, "maxBatchReportLatencyUs and delayUs should be non-negative");
110            return false;
111        }
112
113        // Invariants to preserve:
114        // - one Looper per SensorEventListener
115        // - one Looper per SensorEventQueue
116        // We map SensorEventListener to a SensorEventQueue, which holds the looper
117        synchronized (mSensorListeners) {
118            SensorEventQueue queue = mSensorListeners.get(listener);
119            if (queue == null) {
120                Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
121                queue = new SensorEventQueue(listener, looper, this);
122                if (!queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs)) {
123                    queue.dispose();
124                    return false;
125                }
126                mSensorListeners.put(listener, queue);
127                return true;
128            } else {
129                return queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs);
130            }
131        }
132    }
133
134    /** @hide */
135    @Override
136    protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
137        // Trigger Sensors should use the cancelTriggerSensor call.
138        if (sensor != null && sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
139            return;
140        }
141
142        synchronized (mSensorListeners) {
143            SensorEventQueue queue = mSensorListeners.get(listener);
144            if (queue != null) {
145                boolean result;
146                if (sensor == null) {
147                    result = queue.removeAllSensors();
148                } else {
149                    result = queue.removeSensor(sensor, true);
150                }
151                if (result && !queue.hasSensors()) {
152                    mSensorListeners.remove(listener);
153                    queue.dispose();
154                }
155            }
156        }
157    }
158
159    /** @hide */
160    @Override
161    protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
162        if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
163
164        if (sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) return false;
165
166        synchronized (mTriggerListeners) {
167            TriggerEventQueue queue = mTriggerListeners.get(listener);
168            if (queue == null) {
169                queue = new TriggerEventQueue(listener, mMainLooper, this);
170                if (!queue.addSensor(sensor, 0, 0)) {
171                    queue.dispose();
172                    return false;
173                }
174                mTriggerListeners.put(listener, queue);
175                return true;
176            } else {
177                return queue.addSensor(sensor, 0, 0);
178            }
179        }
180    }
181
182    /** @hide */
183    @Override
184    protected boolean cancelTriggerSensorImpl(TriggerEventListener listener, Sensor sensor,
185            boolean disable) {
186        if (sensor != null && sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) {
187            return false;
188        }
189        synchronized (mTriggerListeners) {
190            TriggerEventQueue queue = mTriggerListeners.get(listener);
191            if (queue != null) {
192                boolean result;
193                if (sensor == null) {
194                    result = queue.removeAllSensors();
195                } else {
196                    result = queue.removeSensor(sensor, disable);
197                }
198                if (result && !queue.hasSensors()) {
199                    mTriggerListeners.remove(listener);
200                    queue.dispose();
201                }
202                return result;
203            }
204            return false;
205        }
206    }
207
208    protected boolean flushImpl(SensorEventListener listener) {
209        if (listener == null) throw new IllegalArgumentException("listener cannot be null");
210
211        synchronized (mSensorListeners) {
212            SensorEventQueue queue = mSensorListeners.get(listener);
213            if (queue == null) {
214                return false;
215            } else {
216                return (queue.flush() == 0);
217            }
218        }
219    }
220
221    /*
222     * BaseEventQueue is the communication channel with the sensor service,
223     * SensorEventQueue, TriggerEventQueue are subclases and there is one-to-one mapping between
224     * the queues and the listeners.
225     */
226    private static abstract class BaseEventQueue {
227        private native long nativeInitBaseEventQueue(BaseEventQueue eventQ, MessageQueue msgQ,
228                float[] scratch, String packageName);
229        private static native int nativeEnableSensor(long eventQ, int handle, int rateUs,
230                int maxBatchReportLatencyUs);
231        private static native int nativeDisableSensor(long eventQ, int handle);
232        private static native void nativeDestroySensorEventQueue(long eventQ);
233        private static native int nativeFlushSensor(long eventQ);
234        private long nSensorEventQueue;
235        private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
236        protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
237        protected final SparseBooleanArray mFirstEvent = new SparseBooleanArray();
238        private final CloseGuard mCloseGuard = CloseGuard.get();
239        private final float[] mScratch = new float[16];
240        protected final SystemSensorManager mManager;
241
242        BaseEventQueue(Looper looper, SystemSensorManager manager) {
243            nSensorEventQueue = nativeInitBaseEventQueue(this, looper.getQueue(), mScratch,
244                    manager.mPackageName);
245            mCloseGuard.open("dispose");
246            mManager = manager;
247        }
248
249        public void dispose() {
250            dispose(false);
251        }
252
253        public boolean addSensor(
254                Sensor sensor, int delayUs, int maxBatchReportLatencyUs) {
255            // Check if already present.
256            int handle = sensor.getHandle();
257            if (mActiveSensors.get(handle)) return false;
258
259            // Get ready to receive events before calling enable.
260            mActiveSensors.put(handle, true);
261            addSensorEvent(sensor);
262            if (enableSensor(sensor, delayUs, maxBatchReportLatencyUs) != 0) {
263                // Try continuous mode if batching fails.
264                if (maxBatchReportLatencyUs == 0 ||
265                    maxBatchReportLatencyUs > 0 && enableSensor(sensor, delayUs, 0) != 0) {
266                  removeSensor(sensor, false);
267                  return false;
268                }
269            }
270            return true;
271        }
272
273        public boolean removeAllSensors() {
274            for (int i=0 ; i<mActiveSensors.size(); i++) {
275                if (mActiveSensors.valueAt(i) == true) {
276                    int handle = mActiveSensors.keyAt(i);
277                    Sensor sensor = sHandleToSensor.get(handle);
278                    if (sensor != null) {
279                        disableSensor(sensor);
280                        mActiveSensors.put(handle, false);
281                        removeSensorEvent(sensor);
282                    } else {
283                        // it should never happen -- just ignore.
284                    }
285                }
286            }
287            return true;
288        }
289
290        public boolean removeSensor(Sensor sensor, boolean disable) {
291            final int handle = sensor.getHandle();
292            if (mActiveSensors.get(handle)) {
293                if (disable) disableSensor(sensor);
294                mActiveSensors.put(sensor.getHandle(), false);
295                removeSensorEvent(sensor);
296                return true;
297            }
298            return false;
299        }
300
301        public int flush() {
302            if (nSensorEventQueue == 0) throw new NullPointerException();
303            return nativeFlushSensor(nSensorEventQueue);
304        }
305
306        public boolean hasSensors() {
307            // no more sensors are set
308            return mActiveSensors.indexOfValue(true) >= 0;
309        }
310
311        @Override
312        protected void finalize() throws Throwable {
313            try {
314                dispose(true);
315            } finally {
316                super.finalize();
317            }
318        }
319
320        private void dispose(boolean finalized) {
321            if (mCloseGuard != null) {
322                if (finalized) {
323                    mCloseGuard.warnIfOpen();
324                }
325                mCloseGuard.close();
326            }
327            if (nSensorEventQueue != 0) {
328                nativeDestroySensorEventQueue(nSensorEventQueue);
329                nSensorEventQueue = 0;
330            }
331        }
332
333        private int enableSensor(
334                Sensor sensor, int rateUs, int maxBatchReportLatencyUs) {
335            if (nSensorEventQueue == 0) throw new NullPointerException();
336            if (sensor == null) throw new NullPointerException();
337            return nativeEnableSensor(nSensorEventQueue, sensor.getHandle(), rateUs,
338                    maxBatchReportLatencyUs);
339        }
340
341        private int disableSensor(Sensor sensor) {
342            if (nSensorEventQueue == 0) throw new NullPointerException();
343            if (sensor == null) throw new NullPointerException();
344            return nativeDisableSensor(nSensorEventQueue, sensor.getHandle());
345        }
346        protected abstract void dispatchSensorEvent(int handle, float[] values, int accuracy,
347                long timestamp);
348        protected abstract void dispatchFlushCompleteEvent(int handle);
349
350        protected abstract void addSensorEvent(Sensor sensor);
351        protected abstract void removeSensorEvent(Sensor sensor);
352    }
353
354    static final class SensorEventQueue extends BaseEventQueue {
355        private final SensorEventListener mListener;
356        private final SparseArray<SensorEvent> mSensorsEvents = new SparseArray<SensorEvent>();
357
358        public SensorEventQueue(SensorEventListener listener, Looper looper,
359                SystemSensorManager manager) {
360            super(looper, manager);
361            mListener = listener;
362        }
363
364        @Override
365        public void addSensorEvent(Sensor sensor) {
366            SensorEvent t = new SensorEvent(Sensor.getMaxLengthValuesArray(sensor,
367                    mManager.mTargetSdkLevel));
368            synchronized (mSensorsEvents) {
369                mSensorsEvents.put(sensor.getHandle(), t);
370            }
371        }
372
373        @Override
374        public void removeSensorEvent(Sensor sensor) {
375            synchronized (mSensorsEvents) {
376                mSensorsEvents.delete(sensor.getHandle());
377            }
378        }
379
380        // Called from native code.
381        @SuppressWarnings("unused")
382        @Override
383        protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy,
384                long timestamp) {
385            final Sensor sensor = sHandleToSensor.get(handle);
386            SensorEvent t = null;
387            synchronized (mSensorsEvents) {
388                t = mSensorsEvents.get(handle);
389            }
390
391            if (t == null) {
392                // This may happen if the client has unregistered and there are pending events in
393                // the queue waiting to be delivered. Ignore.
394                return;
395            }
396            // Copy from the values array.
397            System.arraycopy(values, 0, t.values, 0, t.values.length);
398            t.timestamp = timestamp;
399            t.accuracy = inAccuracy;
400            t.sensor = sensor;
401
402            // call onAccuracyChanged() only if the value changes
403            final int accuracy = mSensorAccuracies.get(handle);
404            if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
405                mSensorAccuracies.put(handle, t.accuracy);
406                mListener.onAccuracyChanged(t.sensor, t.accuracy);
407            }
408            mListener.onSensorChanged(t);
409        }
410
411        @SuppressWarnings("unused")
412        protected void dispatchFlushCompleteEvent(int handle) {
413            if (mListener instanceof SensorEventListener2) {
414                final Sensor sensor = sHandleToSensor.get(handle);
415                ((SensorEventListener2)mListener).onFlushCompleted(sensor);
416            }
417            return;
418        }
419    }
420
421    static final class TriggerEventQueue extends BaseEventQueue {
422        private final TriggerEventListener mListener;
423        private final SparseArray<TriggerEvent> mTriggerEvents = new SparseArray<TriggerEvent>();
424
425        public TriggerEventQueue(TriggerEventListener listener, Looper looper,
426                SystemSensorManager manager) {
427            super(looper, manager);
428            mListener = listener;
429        }
430
431        @Override
432        public void addSensorEvent(Sensor sensor) {
433            TriggerEvent t = new TriggerEvent(Sensor.getMaxLengthValuesArray(sensor,
434                    mManager.mTargetSdkLevel));
435            synchronized (mTriggerEvents) {
436                mTriggerEvents.put(sensor.getHandle(), t);
437            }
438        }
439
440        @Override
441        public void removeSensorEvent(Sensor sensor) {
442            synchronized (mTriggerEvents) {
443                mTriggerEvents.delete(sensor.getHandle());
444            }
445        }
446
447        // Called from native code.
448        @SuppressWarnings("unused")
449        @Override
450        protected void dispatchSensorEvent(int handle, float[] values, int accuracy,
451                long timestamp) {
452            final Sensor sensor = sHandleToSensor.get(handle);
453            TriggerEvent t = null;
454            synchronized (mTriggerEvents) {
455                t = mTriggerEvents.get(handle);
456            }
457            if (t == null) {
458                Log.e(TAG, "Error: Trigger Event is null for Sensor: " + sensor);
459                return;
460            }
461
462            // Copy from the values array.
463            System.arraycopy(values, 0, t.values, 0, t.values.length);
464            t.timestamp = timestamp;
465            t.sensor = sensor;
466
467            // A trigger sensor is auto disabled. So just clean up and don't call native
468            // disable.
469            mManager.cancelTriggerSensorImpl(mListener, sensor, false);
470
471            mListener.onTrigger(t);
472        }
473
474        @SuppressWarnings("unused")
475        protected void dispatchFlushCompleteEvent(int handle) {
476        }
477    }
478}
479