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.Manifest;
20import android.content.Context;
21import android.content.pm.PackageManager;
22import android.os.Handler;
23import android.os.Looper;
24import android.os.MessageQueue;
25import android.util.Log;
26import android.util.SparseArray;
27import android.util.SparseBooleanArray;
28import android.util.SparseIntArray;
29import dalvik.system.CloseGuard;
30
31import java.lang.ref.WeakReference;
32import java.util.ArrayList;
33import java.util.HashMap;
34import java.util.List;
35
36/**
37 * Sensor manager implementation that communicates with the built-in
38 * system sensors.
39 *
40 * @hide
41 */
42public class SystemSensorManager extends SensorManager {
43    private static native void nativeClassInit();
44    private static native long nativeCreate(String opPackageName);
45    private static native boolean nativeGetSensorAtIndex(long nativeInstance,
46            Sensor sensor, int index);
47    private static native boolean nativeIsDataInjectionEnabled(long nativeInstance);
48
49    private static boolean sSensorModuleInitialized = false;
50    private static InjectEventQueue mInjectEventQueue = null;
51
52    private final Object mLock = new Object();
53
54    private final ArrayList<Sensor> mFullSensorsList = new ArrayList<>();
55    private final SparseArray<Sensor> mHandleToSensor = new SparseArray<>();
56
57    // Listener list
58    private final HashMap<SensorEventListener, SensorEventQueue> mSensorListeners =
59            new HashMap<SensorEventListener, SensorEventQueue>();
60    private final HashMap<TriggerEventListener, TriggerEventQueue> mTriggerListeners =
61            new HashMap<TriggerEventListener, TriggerEventQueue>();
62
63    // Looper associated with the context in which this instance was created.
64    private final Looper mMainLooper;
65    private final int mTargetSdkLevel;
66    private final Context mContext;
67    private final long mNativeInstance;
68
69    /** {@hide} */
70    public SystemSensorManager(Context context, Looper mainLooper) {
71        mMainLooper = mainLooper;
72        mTargetSdkLevel = context.getApplicationInfo().targetSdkVersion;
73        mContext = context;
74        mNativeInstance = nativeCreate(context.getOpPackageName());
75
76        synchronized(mLock) {
77            if (!sSensorModuleInitialized) {
78                sSensorModuleInitialized = true;
79                nativeClassInit();
80            }
81        }
82
83        // initialize the sensor list
84        for (int index = 0;;++index) {
85            Sensor sensor = new Sensor();
86            if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break;
87            mFullSensorsList.add(sensor);
88            mHandleToSensor.append(sensor.getHandle(), sensor);
89        }
90    }
91
92
93    /** @hide */
94    @Override
95    protected List<Sensor> getFullSensorList() {
96        return mFullSensorsList;
97    }
98
99
100    /** @hide */
101    @Override
102    protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
103            int delayUs, Handler handler, int maxBatchReportLatencyUs, int reservedFlags) {
104        if (listener == null || sensor == null) {
105            Log.e(TAG, "sensor or listener is null");
106            return false;
107        }
108        // Trigger Sensors should use the requestTriggerSensor call.
109        if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
110            Log.e(TAG, "Trigger Sensors should use the requestTriggerSensor.");
111            return false;
112        }
113        if (maxBatchReportLatencyUs < 0 || delayUs < 0) {
114            Log.e(TAG, "maxBatchReportLatencyUs and delayUs should be non-negative");
115            return false;
116        }
117
118        // Invariants to preserve:
119        // - one Looper per SensorEventListener
120        // - one Looper per SensorEventQueue
121        // We map SensorEventListener to a SensorEventQueue, which holds the looper
122        synchronized (mSensorListeners) {
123            SensorEventQueue queue = mSensorListeners.get(listener);
124            if (queue == null) {
125                Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
126                final String fullClassName = listener.getClass().getEnclosingClass() != null ?
127                    listener.getClass().getEnclosingClass().getName() :
128                    listener.getClass().getName();
129                queue = new SensorEventQueue(listener, looper, this, fullClassName);
130                if (!queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs)) {
131                    queue.dispose();
132                    return false;
133                }
134                mSensorListeners.put(listener, queue);
135                return true;
136            } else {
137                return queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs);
138            }
139        }
140    }
141
142    /** @hide */
143    @Override
144    protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
145        // Trigger Sensors should use the cancelTriggerSensor call.
146        if (sensor != null && sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
147            return;
148        }
149
150        synchronized (mSensorListeners) {
151            SensorEventQueue queue = mSensorListeners.get(listener);
152            if (queue != null) {
153                boolean result;
154                if (sensor == null) {
155                    result = queue.removeAllSensors();
156                } else {
157                    result = queue.removeSensor(sensor, true);
158                }
159                if (result && !queue.hasSensors()) {
160                    mSensorListeners.remove(listener);
161                    queue.dispose();
162                }
163            }
164        }
165    }
166
167    /** @hide */
168    @Override
169    protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
170        if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
171
172        if (listener == null) throw new IllegalArgumentException("listener cannot be null");
173
174        if (sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) return false;
175
176        synchronized (mTriggerListeners) {
177            TriggerEventQueue queue = mTriggerListeners.get(listener);
178            if (queue == null) {
179                final String fullClassName = listener.getClass().getEnclosingClass() != null ?
180                    listener.getClass().getEnclosingClass().getName() :
181                    listener.getClass().getName();
182                queue = new TriggerEventQueue(listener, mMainLooper, this, fullClassName);
183                if (!queue.addSensor(sensor, 0, 0)) {
184                    queue.dispose();
185                    return false;
186                }
187                mTriggerListeners.put(listener, queue);
188                return true;
189            } else {
190                return queue.addSensor(sensor, 0, 0);
191            }
192        }
193    }
194
195    /** @hide */
196    @Override
197    protected boolean cancelTriggerSensorImpl(TriggerEventListener listener, Sensor sensor,
198            boolean disable) {
199        if (sensor != null && sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) {
200            return false;
201        }
202        synchronized (mTriggerListeners) {
203            TriggerEventQueue queue = mTriggerListeners.get(listener);
204            if (queue != null) {
205                boolean result;
206                if (sensor == null) {
207                    result = queue.removeAllSensors();
208                } else {
209                    result = queue.removeSensor(sensor, disable);
210                }
211                if (result && !queue.hasSensors()) {
212                    mTriggerListeners.remove(listener);
213                    queue.dispose();
214                }
215                return result;
216            }
217            return false;
218        }
219    }
220
221    protected boolean flushImpl(SensorEventListener listener) {
222        if (listener == null) throw new IllegalArgumentException("listener cannot be null");
223
224        synchronized (mSensorListeners) {
225            SensorEventQueue queue = mSensorListeners.get(listener);
226            if (queue == null) {
227                return false;
228            } else {
229                return (queue.flush() == 0);
230            }
231        }
232    }
233
234    protected boolean initDataInjectionImpl(boolean enable) {
235        synchronized (mLock) {
236            if (enable) {
237                boolean isDataInjectionModeEnabled = nativeIsDataInjectionEnabled(mNativeInstance);
238                // The HAL does not support injection OR SensorService hasn't been set in DI mode.
239                if (!isDataInjectionModeEnabled) {
240                    Log.e(TAG, "Data Injection mode not enabled");
241                    return false;
242                }
243                // Initialize a client for data_injection.
244                if (mInjectEventQueue == null) {
245                    mInjectEventQueue = new InjectEventQueue(mMainLooper, this,
246                            mContext.getPackageName());
247                }
248            } else {
249                // If data injection is being disabled clean up the native resources.
250                if (mInjectEventQueue != null) {
251                    mInjectEventQueue.dispose();
252                    mInjectEventQueue = null;
253                }
254            }
255            return true;
256        }
257    }
258
259    protected boolean injectSensorDataImpl(Sensor sensor, float[] values, int accuracy,
260            long timestamp) {
261        synchronized (mLock) {
262            if (mInjectEventQueue == null) {
263                Log.e(TAG, "Data injection mode not activated before calling injectSensorData");
264                return false;
265            }
266            int ret = mInjectEventQueue.injectSensorData(sensor.getHandle(), values, accuracy,
267                                                         timestamp);
268            // If there are any errors in data injection clean up the native resources.
269            if (ret != 0) {
270                mInjectEventQueue.dispose();
271                mInjectEventQueue = null;
272            }
273            return ret == 0;
274        }
275    }
276
277    /*
278     * BaseEventQueue is the communication channel with the sensor service,
279     * SensorEventQueue, TriggerEventQueue are subclases and there is one-to-one mapping between
280     * the queues and the listeners. InjectEventQueue is also a sub-class which is a special case
281     * where data is being injected into the sensor HAL through the sensor service. It is not
282     * associated with any listener and there is one InjectEventQueue associated with a
283     * SensorManager instance.
284     */
285    private static abstract class BaseEventQueue {
286        private static native long nativeInitBaseEventQueue(long nativeManager,
287                WeakReference<BaseEventQueue> eventQWeak, MessageQueue msgQ, float[] scratch,
288                String packageName, int mode, String opPackageName);
289        private static native int nativeEnableSensor(long eventQ, int handle, int rateUs,
290                int maxBatchReportLatencyUs);
291        private static native int nativeDisableSensor(long eventQ, int handle);
292        private static native void nativeDestroySensorEventQueue(long eventQ);
293        private static native int nativeFlushSensor(long eventQ);
294        private static native int nativeInjectSensorData(long eventQ, int handle,
295                float[] values,int accuracy, long timestamp);
296
297        private long nSensorEventQueue;
298        private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
299        protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
300        protected final SparseBooleanArray mFirstEvent = new SparseBooleanArray();
301        private final CloseGuard mCloseGuard = CloseGuard.get();
302        private final float[] mScratch = new float[16];
303        protected final SystemSensorManager mManager;
304
305        protected static final int OPERATING_MODE_NORMAL = 0;
306        protected static final int OPERATING_MODE_DATA_INJECTION = 1;
307
308        BaseEventQueue(Looper looper, SystemSensorManager manager, int mode, String packageName) {
309            if (packageName == null) packageName = "";
310            nSensorEventQueue = nativeInitBaseEventQueue(manager.mNativeInstance,
311                    new WeakReference<>(this), looper.getQueue(), mScratch,
312                    packageName, mode, manager.mContext.getOpPackageName());
313            mCloseGuard.open("dispose");
314            mManager = manager;
315        }
316
317        public void dispose() {
318            dispose(false);
319        }
320
321        public boolean addSensor(
322                Sensor sensor, int delayUs, int maxBatchReportLatencyUs) {
323            // Check if already present.
324            int handle = sensor.getHandle();
325            if (mActiveSensors.get(handle)) return false;
326
327            // Get ready to receive events before calling enable.
328            mActiveSensors.put(handle, true);
329            addSensorEvent(sensor);
330            if (enableSensor(sensor, delayUs, maxBatchReportLatencyUs) != 0) {
331                // Try continuous mode if batching fails.
332                if (maxBatchReportLatencyUs == 0 ||
333                    maxBatchReportLatencyUs > 0 && enableSensor(sensor, delayUs, 0) != 0) {
334                  removeSensor(sensor, false);
335                  return false;
336                }
337            }
338            return true;
339        }
340
341        public boolean removeAllSensors() {
342            for (int i=0 ; i<mActiveSensors.size(); i++) {
343                if (mActiveSensors.valueAt(i) == true) {
344                    int handle = mActiveSensors.keyAt(i);
345                    Sensor sensor = mManager.mHandleToSensor.get(handle);
346                    if (sensor != null) {
347                        disableSensor(sensor);
348                        mActiveSensors.put(handle, false);
349                        removeSensorEvent(sensor);
350                    } else {
351                        // it should never happen -- just ignore.
352                    }
353                }
354            }
355            return true;
356        }
357
358        public boolean removeSensor(Sensor sensor, boolean disable) {
359            final int handle = sensor.getHandle();
360            if (mActiveSensors.get(handle)) {
361                if (disable) disableSensor(sensor);
362                mActiveSensors.put(sensor.getHandle(), false);
363                removeSensorEvent(sensor);
364                return true;
365            }
366            return false;
367        }
368
369        public int flush() {
370            if (nSensorEventQueue == 0) throw new NullPointerException();
371            return nativeFlushSensor(nSensorEventQueue);
372        }
373
374        public boolean hasSensors() {
375            // no more sensors are set
376            return mActiveSensors.indexOfValue(true) >= 0;
377        }
378
379        @Override
380        protected void finalize() throws Throwable {
381            try {
382                dispose(true);
383            } finally {
384                super.finalize();
385            }
386        }
387
388        private void dispose(boolean finalized) {
389            if (mCloseGuard != null) {
390                if (finalized) {
391                    mCloseGuard.warnIfOpen();
392                }
393                mCloseGuard.close();
394            }
395            if (nSensorEventQueue != 0) {
396                nativeDestroySensorEventQueue(nSensorEventQueue);
397                nSensorEventQueue = 0;
398            }
399        }
400
401        private int enableSensor(
402                Sensor sensor, int rateUs, int maxBatchReportLatencyUs) {
403            if (nSensorEventQueue == 0) throw new NullPointerException();
404            if (sensor == null) throw new NullPointerException();
405            return nativeEnableSensor(nSensorEventQueue, sensor.getHandle(), rateUs,
406                    maxBatchReportLatencyUs);
407        }
408
409        protected int injectSensorDataBase(int handle, float[] values, int accuracy,
410                                           long timestamp) {
411            return nativeInjectSensorData(nSensorEventQueue, handle, values, accuracy, timestamp);
412        }
413
414        private int disableSensor(Sensor sensor) {
415            if (nSensorEventQueue == 0) throw new NullPointerException();
416            if (sensor == null) throw new NullPointerException();
417            return nativeDisableSensor(nSensorEventQueue, sensor.getHandle());
418        }
419        protected abstract void dispatchSensorEvent(int handle, float[] values, int accuracy,
420                long timestamp);
421        protected abstract void dispatchFlushCompleteEvent(int handle);
422
423        protected abstract void addSensorEvent(Sensor sensor);
424        protected abstract void removeSensorEvent(Sensor sensor);
425    }
426
427    static final class SensorEventQueue extends BaseEventQueue {
428        private final SensorEventListener mListener;
429        private final SparseArray<SensorEvent> mSensorsEvents = new SparseArray<SensorEvent>();
430
431        public SensorEventQueue(SensorEventListener listener, Looper looper,
432                SystemSensorManager manager, String packageName) {
433            super(looper, manager, OPERATING_MODE_NORMAL, packageName);
434            mListener = listener;
435        }
436
437        @Override
438        public void addSensorEvent(Sensor sensor) {
439            SensorEvent t = new SensorEvent(Sensor.getMaxLengthValuesArray(sensor,
440                    mManager.mTargetSdkLevel));
441            synchronized (mSensorsEvents) {
442                mSensorsEvents.put(sensor.getHandle(), t);
443            }
444        }
445
446        @Override
447        public void removeSensorEvent(Sensor sensor) {
448            synchronized (mSensorsEvents) {
449                mSensorsEvents.delete(sensor.getHandle());
450            }
451        }
452
453        // Called from native code.
454        @SuppressWarnings("unused")
455        @Override
456        protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy,
457                long timestamp) {
458            final Sensor sensor = mManager.mHandleToSensor.get(handle);
459            SensorEvent t = null;
460            synchronized (mSensorsEvents) {
461                t = mSensorsEvents.get(handle);
462            }
463
464            if (t == null) {
465                // This may happen if the client has unregistered and there are pending events in
466                // the queue waiting to be delivered. Ignore.
467                return;
468            }
469            // Copy from the values array.
470            System.arraycopy(values, 0, t.values, 0, t.values.length);
471            t.timestamp = timestamp;
472            t.accuracy = inAccuracy;
473            t.sensor = sensor;
474
475            // call onAccuracyChanged() only if the value changes
476            final int accuracy = mSensorAccuracies.get(handle);
477            if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
478                mSensorAccuracies.put(handle, t.accuracy);
479                mListener.onAccuracyChanged(t.sensor, t.accuracy);
480            }
481            mListener.onSensorChanged(t);
482        }
483
484        @SuppressWarnings("unused")
485        protected void dispatchFlushCompleteEvent(int handle) {
486            if (mListener instanceof SensorEventListener2) {
487                final Sensor sensor = mManager.mHandleToSensor.get(handle);
488                ((SensorEventListener2)mListener).onFlushCompleted(sensor);
489            }
490            return;
491        }
492    }
493
494    static final class TriggerEventQueue extends BaseEventQueue {
495        private final TriggerEventListener mListener;
496        private final SparseArray<TriggerEvent> mTriggerEvents = new SparseArray<TriggerEvent>();
497
498        public TriggerEventQueue(TriggerEventListener listener, Looper looper,
499                SystemSensorManager manager, String packageName) {
500            super(looper, manager, OPERATING_MODE_NORMAL, packageName);
501            mListener = listener;
502        }
503
504        @Override
505        public void addSensorEvent(Sensor sensor) {
506            TriggerEvent t = new TriggerEvent(Sensor.getMaxLengthValuesArray(sensor,
507                    mManager.mTargetSdkLevel));
508            synchronized (mTriggerEvents) {
509                mTriggerEvents.put(sensor.getHandle(), t);
510            }
511        }
512
513        @Override
514        public void removeSensorEvent(Sensor sensor) {
515            synchronized (mTriggerEvents) {
516                mTriggerEvents.delete(sensor.getHandle());
517            }
518        }
519
520        // Called from native code.
521        @SuppressWarnings("unused")
522        @Override
523        protected void dispatchSensorEvent(int handle, float[] values, int accuracy,
524                long timestamp) {
525            final Sensor sensor = mManager.mHandleToSensor.get(handle);
526            TriggerEvent t = null;
527            synchronized (mTriggerEvents) {
528                t = mTriggerEvents.get(handle);
529            }
530            if (t == null) {
531                Log.e(TAG, "Error: Trigger Event is null for Sensor: " + sensor);
532                return;
533            }
534
535            // Copy from the values array.
536            System.arraycopy(values, 0, t.values, 0, t.values.length);
537            t.timestamp = timestamp;
538            t.sensor = sensor;
539
540            // A trigger sensor is auto disabled. So just clean up and don't call native
541            // disable.
542            mManager.cancelTriggerSensorImpl(mListener, sensor, false);
543
544            mListener.onTrigger(t);
545        }
546
547        @SuppressWarnings("unused")
548        protected void dispatchFlushCompleteEvent(int handle) {
549        }
550    }
551
552    final class InjectEventQueue extends BaseEventQueue {
553        public InjectEventQueue(Looper looper, SystemSensorManager manager, String packageName) {
554            super(looper, manager, OPERATING_MODE_DATA_INJECTION, packageName);
555        }
556
557        int injectSensorData(int handle, float[] values,int accuracy, long timestamp) {
558             return injectSensorDataBase(handle, values, accuracy, timestamp);
559        }
560
561        @SuppressWarnings("unused")
562        protected void dispatchSensorEvent(int handle, float[] values, int accuracy,
563                long timestamp) {
564        }
565
566        @SuppressWarnings("unused")
567        protected void dispatchFlushCompleteEvent(int handle) {
568
569        }
570
571        @SuppressWarnings("unused")
572        protected void addSensorEvent(Sensor sensor) {
573
574        }
575
576        @SuppressWarnings("unused")
577        protected void removeSensorEvent(Sensor sensor) {
578
579        }
580    }
581}
582