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 static android.view.Display.DEFAULT_DISPLAY;
20
21import android.os.RemoteException;
22import android.os.ServiceManager;
23import android.view.IRotationWatcher;
24import android.view.IWindowManager;
25import android.view.Surface;
26
27import java.util.HashMap;
28import java.util.List;
29
30/**
31 * Helper class for implementing the legacy sensor manager API.
32 * @hide
33 */
34@SuppressWarnings("deprecation")
35final class LegacySensorManager {
36    private static boolean sInitialized;
37    private static IWindowManager sWindowManager;
38    private static int sRotation = Surface.ROTATION_0;
39
40    private final SensorManager mSensorManager;
41
42    // List of legacy listeners.  Guarded by mLegacyListenersMap.
43    private final HashMap<SensorListener, LegacyListener> mLegacyListenersMap =
44            new HashMap<SensorListener, LegacyListener>();
45
46    public LegacySensorManager(SensorManager sensorManager) {
47        mSensorManager = sensorManager;
48
49        synchronized (SensorManager.class) {
50            if (!sInitialized) {
51                sWindowManager = IWindowManager.Stub.asInterface(
52                        ServiceManager.getService("window"));
53                if (sWindowManager != null) {
54                    // if it's null we're running in the system process
55                    // which won't get the rotated values
56                    try {
57                        sRotation = sWindowManager.watchRotation(
58                                new IRotationWatcher.Stub() {
59                                    public void onRotationChanged(int rotation) {
60                                        LegacySensorManager.onRotationChanged(rotation);
61                                    }
62                                }, DEFAULT_DISPLAY);
63                    } catch (RemoteException e) {
64                    }
65                }
66            }
67        }
68    }
69
70    public int getSensors() {
71        int result = 0;
72        final List<Sensor> fullList = mSensorManager.getFullSensorList();
73        for (Sensor i : fullList) {
74            switch (i.getType()) {
75                case Sensor.TYPE_ACCELEROMETER:
76                    result |= SensorManager.SENSOR_ACCELEROMETER;
77                    break;
78                case Sensor.TYPE_MAGNETIC_FIELD:
79                    result |= SensorManager.SENSOR_MAGNETIC_FIELD;
80                    break;
81                case Sensor.TYPE_ORIENTATION:
82                    result |= SensorManager.SENSOR_ORIENTATION
83                            | SensorManager.SENSOR_ORIENTATION_RAW;
84                    break;
85            }
86        }
87        return result;
88    }
89
90    public boolean registerListener(SensorListener listener, int sensors, int rate) {
91        if (listener == null) {
92            return false;
93        }
94        boolean result = false;
95        result = registerLegacyListener(SensorManager.SENSOR_ACCELEROMETER,
96                Sensor.TYPE_ACCELEROMETER, listener, sensors, rate) || result;
97        result = registerLegacyListener(SensorManager.SENSOR_MAGNETIC_FIELD,
98                Sensor.TYPE_MAGNETIC_FIELD, listener, sensors, rate) || result;
99        result = registerLegacyListener(SensorManager.SENSOR_ORIENTATION_RAW,
100                Sensor.TYPE_ORIENTATION, listener, sensors, rate) || result;
101        result = registerLegacyListener(SensorManager.SENSOR_ORIENTATION,
102                Sensor.TYPE_ORIENTATION, listener, sensors, rate) || result;
103        result = registerLegacyListener(SensorManager.SENSOR_TEMPERATURE,
104                Sensor.TYPE_TEMPERATURE, listener, sensors, rate) || result;
105        return result;
106    }
107
108    private boolean registerLegacyListener(int legacyType, int type,
109            SensorListener listener, int sensors, int rate) {
110        boolean result = false;
111        // Are we activating this legacy sensor?
112        if ((sensors & legacyType) != 0) {
113            // if so, find a suitable Sensor
114            Sensor sensor = mSensorManager.getDefaultSensor(type);
115            if (sensor != null) {
116                // We do all of this work holding the legacy listener lock to ensure
117                // that the invariants around listeners are maintained.  This is safe
118                // because neither registerLegacyListener nor unregisterLegacyListener
119                // are called reentrantly while sensors are being registered or unregistered.
120                synchronized (mLegacyListenersMap) {
121                    // If we don't already have one, create a LegacyListener
122                    // to wrap this listener and process the events as
123                    // they are expected by legacy apps.
124                    LegacyListener legacyListener = mLegacyListenersMap.get(listener);
125                    if (legacyListener == null) {
126                        // we didn't find a LegacyListener for this client,
127                        // create one, and put it in our list.
128                        legacyListener = new LegacyListener(listener);
129                        mLegacyListenersMap.put(listener, legacyListener);
130                    }
131
132                    // register this legacy sensor with this legacy listener
133                    if (legacyListener.registerSensor(legacyType)) {
134                        // and finally, register the legacy listener with the new apis
135                        result = mSensorManager.registerListener(legacyListener, sensor, rate);
136                    } else {
137                        result = true; // sensor already enabled
138                    }
139                }
140            }
141        }
142        return result;
143    }
144
145    public void unregisterListener(SensorListener listener, int sensors) {
146        if (listener == null) {
147            return;
148        }
149        unregisterLegacyListener(SensorManager.SENSOR_ACCELEROMETER, Sensor.TYPE_ACCELEROMETER,
150                listener, sensors);
151        unregisterLegacyListener(SensorManager.SENSOR_MAGNETIC_FIELD, Sensor.TYPE_MAGNETIC_FIELD,
152                listener, sensors);
153        unregisterLegacyListener(SensorManager.SENSOR_ORIENTATION_RAW, Sensor.TYPE_ORIENTATION,
154                listener, sensors);
155        unregisterLegacyListener(SensorManager.SENSOR_ORIENTATION, Sensor.TYPE_ORIENTATION,
156                listener, sensors);
157        unregisterLegacyListener(SensorManager.SENSOR_TEMPERATURE, Sensor.TYPE_TEMPERATURE,
158                listener, sensors);
159    }
160
161    private void unregisterLegacyListener(int legacyType, int type,
162            SensorListener listener, int sensors) {
163        // Are we deactivating this legacy sensor?
164        if ((sensors & legacyType) != 0) {
165            // if so, find the corresponding Sensor
166            Sensor sensor = mSensorManager.getDefaultSensor(type);
167            if (sensor != null) {
168                // We do all of this work holding the legacy listener lock to ensure
169                // that the invariants around listeners are maintained.  This is safe
170                // because neither registerLegacyListener nor unregisterLegacyListener
171                // are called re-entrantly while sensors are being registered or unregistered.
172                synchronized (mLegacyListenersMap) {
173                    // do we know about this listener?
174                    LegacyListener legacyListener = mLegacyListenersMap.get(listener);
175                    if (legacyListener != null) {
176                        // unregister this legacy sensor and if we don't
177                        // need the corresponding Sensor, unregister it too
178                        if (legacyListener.unregisterSensor(legacyType)) {
179                            // corresponding sensor not needed, unregister
180                            mSensorManager.unregisterListener(legacyListener, sensor);
181
182                            // finally check if we still need the legacyListener
183                            // in our mapping, if not, get rid of it too.
184                            if (!legacyListener.hasSensors()) {
185                                mLegacyListenersMap.remove(listener);
186                            }
187                        }
188                    }
189                }
190            }
191        }
192    }
193
194    static void onRotationChanged(int rotation) {
195        synchronized (SensorManager.class) {
196            sRotation  = rotation;
197        }
198    }
199
200    static int getRotation() {
201        synchronized (SensorManager.class) {
202            return sRotation;
203        }
204    }
205
206    private static final class LegacyListener implements SensorEventListener {
207        private float mValues[] = new float[6];
208        private SensorListener mTarget;
209        private int mSensors;
210        private final LmsFilter mYawfilter = new LmsFilter();
211
212        LegacyListener(SensorListener target) {
213            mTarget = target;
214            mSensors = 0;
215        }
216
217        boolean registerSensor(int legacyType) {
218            if ((mSensors & legacyType) != 0) {
219                return false;
220            }
221            boolean alreadyHasOrientationSensor = hasOrientationSensor(mSensors);
222            mSensors |= legacyType;
223            if (alreadyHasOrientationSensor && hasOrientationSensor(legacyType)) {
224                return false; // don't need to re-register the orientation sensor
225            }
226            return true;
227        }
228
229        boolean unregisterSensor(int legacyType) {
230            if ((mSensors & legacyType) == 0) {
231                return false;
232            }
233            mSensors &= ~legacyType;
234            if (hasOrientationSensor(legacyType) && hasOrientationSensor(mSensors)) {
235                return false; // can't unregister the orientation sensor just yet
236            }
237            return true;
238        }
239
240        boolean hasSensors() {
241            return mSensors != 0;
242        }
243
244        private static boolean hasOrientationSensor(int sensors) {
245            return (sensors & (SensorManager.SENSOR_ORIENTATION
246                    | SensorManager.SENSOR_ORIENTATION_RAW)) != 0;
247        }
248
249        public void onAccuracyChanged(Sensor sensor, int accuracy) {
250            try {
251                mTarget.onAccuracyChanged(getLegacySensorType(sensor.getType()), accuracy);
252            } catch (AbstractMethodError e) {
253                // old app that doesn't implement this method
254                // just ignore it.
255            }
256        }
257
258        public void onSensorChanged(SensorEvent event) {
259            final float v[] = mValues;
260            v[0] = event.values[0];
261            v[1] = event.values[1];
262            v[2] = event.values[2];
263            int type = event.sensor.getType();
264            int legacyType = getLegacySensorType(type);
265            mapSensorDataToWindow(legacyType, v, LegacySensorManager.getRotation());
266            if (type == Sensor.TYPE_ORIENTATION) {
267                if ((mSensors & SensorManager.SENSOR_ORIENTATION_RAW)!=0) {
268                    mTarget.onSensorChanged(SensorManager.SENSOR_ORIENTATION_RAW, v);
269                }
270                if ((mSensors & SensorManager.SENSOR_ORIENTATION)!=0) {
271                    v[0] = mYawfilter.filter(event.timestamp, v[0]);
272                    mTarget.onSensorChanged(SensorManager.SENSOR_ORIENTATION, v);
273                }
274            } else {
275                mTarget.onSensorChanged(legacyType, v);
276            }
277        }
278
279        /*
280         * Helper function to convert the specified sensor's data to the windows's
281         * coordinate space from the device's coordinate space.
282         *
283         * output: 3,4,5: values in the old API format
284         *         0,1,2: transformed values in the old API format
285         *
286         */
287        private void mapSensorDataToWindow(int sensor,
288                float[] values, int orientation) {
289            float x = values[0];
290            float y = values[1];
291            float z = values[2];
292
293            switch (sensor) {
294                case SensorManager.SENSOR_ORIENTATION:
295                case SensorManager.SENSOR_ORIENTATION_RAW:
296                    z = -z;
297                    break;
298                case SensorManager.SENSOR_ACCELEROMETER:
299                    x = -x;
300                    y = -y;
301                    z = -z;
302                    break;
303                case SensorManager.SENSOR_MAGNETIC_FIELD:
304                    x = -x;
305                    y = -y;
306                    break;
307            }
308            values[0] = x;
309            values[1] = y;
310            values[2] = z;
311            values[3] = x;
312            values[4] = y;
313            values[5] = z;
314
315            if ((orientation & Surface.ROTATION_90) != 0) {
316                // handles 90 and 270 rotation
317                switch (sensor) {
318                    case SensorManager.SENSOR_ACCELEROMETER:
319                    case SensorManager.SENSOR_MAGNETIC_FIELD:
320                        values[0] =-y;
321                        values[1] = x;
322                        values[2] = z;
323                        break;
324                    case SensorManager.SENSOR_ORIENTATION:
325                    case SensorManager.SENSOR_ORIENTATION_RAW:
326                        values[0] = x + ((x < 270) ? 90 : -270);
327                        values[1] = z;
328                        values[2] = y;
329                        break;
330                }
331            }
332            if ((orientation & Surface.ROTATION_180) != 0) {
333                x = values[0];
334                y = values[1];
335                z = values[2];
336                // handles 180 (flip) and 270 (flip + 90) rotation
337                switch (sensor) {
338                    case SensorManager.SENSOR_ACCELEROMETER:
339                    case SensorManager.SENSOR_MAGNETIC_FIELD:
340                        values[0] =-x;
341                        values[1] =-y;
342                        values[2] = z;
343                        break;
344                    case SensorManager.SENSOR_ORIENTATION:
345                    case SensorManager.SENSOR_ORIENTATION_RAW:
346                        values[0] = (x >= 180) ? (x - 180) : (x + 180);
347                        values[1] =-y;
348                        values[2] =-z;
349                        break;
350                }
351            }
352        }
353
354        private static int getLegacySensorType(int type) {
355            switch (type) {
356                case Sensor.TYPE_ACCELEROMETER:
357                    return SensorManager.SENSOR_ACCELEROMETER;
358                case Sensor.TYPE_MAGNETIC_FIELD:
359                    return SensorManager.SENSOR_MAGNETIC_FIELD;
360                case Sensor.TYPE_ORIENTATION:
361                    return SensorManager.SENSOR_ORIENTATION_RAW;
362                case Sensor.TYPE_TEMPERATURE:
363                    return SensorManager.SENSOR_TEMPERATURE;
364            }
365            return 0;
366        }
367    }
368
369    private static final class LmsFilter {
370        private static final int SENSORS_RATE_MS = 20;
371        private static final int COUNT = 12;
372        private static final float PREDICTION_RATIO = 1.0f/3.0f;
373        private static final float PREDICTION_TIME = (SENSORS_RATE_MS*COUNT/1000.0f)*PREDICTION_RATIO;
374        private float mV[] = new float[COUNT*2];
375        private long mT[] = new long[COUNT*2];
376        private int mIndex;
377
378        public LmsFilter() {
379            mIndex = COUNT;
380        }
381
382        public float filter(long time, float in) {
383            float v = in;
384            final float ns = 1.0f / 1000000000.0f;
385            float v1 = mV[mIndex];
386            if ((v-v1) > 180) {
387                v -= 360;
388            } else if ((v1-v) > 180) {
389                v += 360;
390            }
391            /* Manage the circular buffer, we write the data twice spaced
392             * by COUNT values, so that we don't have to copy the array
393             * when it's full
394             */
395            mIndex++;
396            if (mIndex >= COUNT*2)
397                mIndex = COUNT;
398            mV[mIndex] = v;
399            mT[mIndex] = time;
400            mV[mIndex-COUNT] = v;
401            mT[mIndex-COUNT] = time;
402
403            float A, B, C, D, E;
404            float a, b;
405            int i;
406
407            A = B = C = D = E = 0;
408            for (i=0 ; i<COUNT-1 ; i++) {
409                final int j = mIndex - 1 - i;
410                final float Z = mV[j];
411                final float T = (mT[j]/2 + mT[j+1]/2 - time)*ns;
412                float dT = (mT[j] - mT[j+1])*ns;
413                dT *= dT;
414                A += Z*dT;
415                B += T*(T*dT);
416                C +=   (T*dT);
417                D += Z*(T*dT);
418                E += dT;
419            }
420            b = (A*B + C*D) / (E*B + C*C);
421            a = (E*b - A) / C;
422            float f = b + PREDICTION_TIME*a;
423
424            // Normalize
425            f *= (1.0f / 360.0f);
426            if (((f>=0)?f:-f) >= 0.5f)
427                f = f - (float)Math.ceil(f + 0.5f) + 1.0f;
428            if (f < 0)
429                f += 1.0f;
430            f *= 360.0f;
431            return f;
432        }
433    }
434}
435