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