Move legacy sensor support to a new class.

Bug: 6339552
Change-Id: I2067b754348ac76b1e1f71608031be2c80fc31d2
diff --git a/core/java/android/hardware/LegacySensorManager.java b/core/java/android/hardware/LegacySensorManager.java
new file mode 100644
index 0000000..62c194f
--- /dev/null
+++ b/core/java/android/hardware/LegacySensorManager.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.view.IRotationWatcher;
+import android.view.IWindowManager;
+import android.view.Surface;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Helper class for implementing the legacy sensor manager API.
+ * @hide
+ */
+@SuppressWarnings("deprecation")
+final class LegacySensorManager {
+    private static boolean sInitialized;
+    private static IWindowManager sWindowManager;
+    private static int sRotation = Surface.ROTATION_0;
+
+    private final SensorManager mSensorManager;
+
+    // List of legacy listeners.  Guarded by mLegacyListenersMap.
+    private final HashMap<SensorListener, LegacyListener> mLegacyListenersMap =
+            new HashMap<SensorListener, LegacyListener>();
+
+    public LegacySensorManager(SensorManager sensorManager) {
+        mSensorManager = sensorManager;
+
+        synchronized (SensorManager.class) {
+            if (!sInitialized) {
+                sWindowManager = IWindowManager.Stub.asInterface(
+                        ServiceManager.getService("window"));
+                if (sWindowManager != null) {
+                    // if it's null we're running in the system process
+                    // which won't get the rotated values
+                    try {
+                        sRotation = sWindowManager.watchRotation(
+                                new IRotationWatcher.Stub() {
+                                    public void onRotationChanged(int rotation) {
+                                        LegacySensorManager.onRotationChanged(rotation);
+                                    }
+                                }
+                        );
+                    } catch (RemoteException e) {
+                    }
+                }
+            }
+        }
+    }
+
+    public int getSensors() {
+        int result = 0;
+        final List<Sensor> fullList = mSensorManager.getFullSensorList();
+        for (Sensor i : fullList) {
+            switch (i.getType()) {
+                case Sensor.TYPE_ACCELEROMETER:
+                    result |= SensorManager.SENSOR_ACCELEROMETER;
+                    break;
+                case Sensor.TYPE_MAGNETIC_FIELD:
+                    result |= SensorManager.SENSOR_MAGNETIC_FIELD;
+                    break;
+                case Sensor.TYPE_ORIENTATION:
+                    result |= SensorManager.SENSOR_ORIENTATION
+                            | SensorManager.SENSOR_ORIENTATION_RAW;
+                    break;
+            }
+        }
+        return result;
+    }
+
+    public boolean registerListener(SensorListener listener, int sensors, int rate) {
+        if (listener == null) {
+            return false;
+        }
+        boolean result = false;
+        result = registerLegacyListener(SensorManager.SENSOR_ACCELEROMETER,
+                Sensor.TYPE_ACCELEROMETER, listener, sensors, rate) || result;
+        result = registerLegacyListener(SensorManager.SENSOR_MAGNETIC_FIELD,
+                Sensor.TYPE_MAGNETIC_FIELD, listener, sensors, rate) || result;
+        result = registerLegacyListener(SensorManager.SENSOR_ORIENTATION_RAW,
+                Sensor.TYPE_ORIENTATION, listener, sensors, rate) || result;
+        result = registerLegacyListener(SensorManager.SENSOR_ORIENTATION,
+                Sensor.TYPE_ORIENTATION, listener, sensors, rate) || result;
+        result = registerLegacyListener(SensorManager.SENSOR_TEMPERATURE,
+                Sensor.TYPE_TEMPERATURE, listener, sensors, rate) || result;
+        return result;
+    }
+
+    private boolean registerLegacyListener(int legacyType, int type,
+            SensorListener listener, int sensors, int rate) {
+        boolean result = false;
+        // Are we activating this legacy sensor?
+        if ((sensors & legacyType) != 0) {
+            // if so, find a suitable Sensor
+            Sensor sensor = mSensorManager.getDefaultSensor(type);
+            if (sensor != null) {
+                // We do all of this work holding the legacy listener lock to ensure
+                // that the invariants around listeners are maintained.  This is safe
+                // because neither registerLegacyListener nor unregisterLegacyListener
+                // are called reentrantly while sensors are being registered or unregistered.
+                synchronized (mLegacyListenersMap) {
+                    // If we don't already have one, create a LegacyListener
+                    // to wrap this listener and process the events as
+                    // they are expected by legacy apps.
+                    LegacyListener legacyListener = mLegacyListenersMap.get(listener);
+                    if (legacyListener == null) {
+                        // we didn't find a LegacyListener for this client,
+                        // create one, and put it in our list.
+                        legacyListener = new LegacyListener(listener);
+                        mLegacyListenersMap.put(listener, legacyListener);
+                    }
+
+                    // register this legacy sensor with this legacy listener
+                    if (legacyListener.registerSensor(legacyType)) {
+                        // and finally, register the legacy listener with the new apis
+                        result = mSensorManager.registerListener(legacyListener, sensor, rate);
+                    } else {
+                        result = true; // sensor already enabled
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+    public void unregisterListener(SensorListener listener, int sensors) {
+        if (listener == null) {
+            return;
+        }
+        unregisterLegacyListener(SensorManager.SENSOR_ACCELEROMETER, Sensor.TYPE_ACCELEROMETER,
+                listener, sensors);
+        unregisterLegacyListener(SensorManager.SENSOR_MAGNETIC_FIELD, Sensor.TYPE_MAGNETIC_FIELD,
+                listener, sensors);
+        unregisterLegacyListener(SensorManager.SENSOR_ORIENTATION_RAW, Sensor.TYPE_ORIENTATION,
+                listener, sensors);
+        unregisterLegacyListener(SensorManager.SENSOR_ORIENTATION, Sensor.TYPE_ORIENTATION,
+                listener, sensors);
+        unregisterLegacyListener(SensorManager.SENSOR_TEMPERATURE, Sensor.TYPE_TEMPERATURE,
+                listener, sensors);
+    }
+
+    private void unregisterLegacyListener(int legacyType, int type,
+            SensorListener listener, int sensors) {
+        // Are we deactivating this legacy sensor?
+        if ((sensors & legacyType) != 0) {
+            // if so, find the corresponding Sensor
+            Sensor sensor = mSensorManager.getDefaultSensor(type);
+            if (sensor != null) {
+                // We do all of this work holding the legacy listener lock to ensure
+                // that the invariants around listeners are maintained.  This is safe
+                // because neither registerLegacyListener nor unregisterLegacyListener
+                // are called re-entrantly while sensors are being registered or unregistered.
+                synchronized (mLegacyListenersMap) {
+                    // do we know about this listener?
+                    LegacyListener legacyListener = mLegacyListenersMap.get(listener);
+                    if (legacyListener != null) {
+                        // unregister this legacy sensor and if we don't
+                        // need the corresponding Sensor, unregister it too
+                        if (legacyListener.unregisterSensor(legacyType)) {
+                            // corresponding sensor not needed, unregister
+                            mSensorManager.unregisterListener(legacyListener, sensor);
+
+                            // finally check if we still need the legacyListener
+                            // in our mapping, if not, get rid of it too.
+                            if (!legacyListener.hasSensors()) {
+                                mLegacyListenersMap.remove(listener);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    static void onRotationChanged(int rotation) {
+        synchronized (SensorManager.class) {
+            sRotation  = rotation;
+        }
+    }
+
+    static int getRotation() {
+        synchronized (SensorManager.class) {
+            return sRotation;
+        }
+    }
+
+    private static final class LegacyListener implements SensorEventListener {
+        private float mValues[] = new float[6];
+        private SensorListener mTarget;
+        private int mSensors;
+        private final LmsFilter mYawfilter = new LmsFilter();
+
+        LegacyListener(SensorListener target) {
+            mTarget = target;
+            mSensors = 0;
+        }
+
+        boolean registerSensor(int legacyType) {
+            if ((mSensors & legacyType) != 0) {
+                return false;
+            }
+            boolean alreadyHasOrientationSensor = hasOrientationSensor(mSensors);
+            mSensors |= legacyType;
+            if (alreadyHasOrientationSensor && hasOrientationSensor(legacyType)) {
+                return false; // don't need to re-register the orientation sensor
+            }
+            return true;
+        }
+
+        boolean unregisterSensor(int legacyType) {
+            if ((mSensors & legacyType) == 0) {
+                return false;
+            }
+            mSensors &= ~legacyType;
+            if (hasOrientationSensor(legacyType) && hasOrientationSensor(mSensors)) {
+                return false; // can't unregister the orientation sensor just yet
+            }
+            return true;
+        }
+
+        boolean hasSensors() {
+            return mSensors != 0;
+        }
+
+        private static boolean hasOrientationSensor(int sensors) {
+            return (sensors & (SensorManager.SENSOR_ORIENTATION
+                    | SensorManager.SENSOR_ORIENTATION_RAW)) != 0;
+        }
+
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+            try {
+                mTarget.onAccuracyChanged(getLegacySensorType(sensor.getType()), accuracy);
+            } catch (AbstractMethodError e) {
+                // old app that doesn't implement this method
+                // just ignore it.
+            }
+        }
+
+        public void onSensorChanged(SensorEvent event) {
+            final float v[] = mValues;
+            v[0] = event.values[0];
+            v[1] = event.values[1];
+            v[2] = event.values[2];
+            int type = event.sensor.getType();
+            int legacyType = getLegacySensorType(type);
+            mapSensorDataToWindow(legacyType, v, LegacySensorManager.getRotation());
+            if (type == Sensor.TYPE_ORIENTATION) {
+                if ((mSensors & SensorManager.SENSOR_ORIENTATION_RAW)!=0) {
+                    mTarget.onSensorChanged(SensorManager.SENSOR_ORIENTATION_RAW, v);
+                }
+                if ((mSensors & SensorManager.SENSOR_ORIENTATION)!=0) {
+                    v[0] = mYawfilter.filter(event.timestamp, v[0]);
+                    mTarget.onSensorChanged(SensorManager.SENSOR_ORIENTATION, v);
+                }
+            } else {
+                mTarget.onSensorChanged(legacyType, v);
+            }
+        }
+
+        /*
+         * Helper function to convert the specified sensor's data to the windows's
+         * coordinate space from the device's coordinate space.
+         *
+         * output: 3,4,5: values in the old API format
+         *         0,1,2: transformed values in the old API format
+         *
+         */
+        private void mapSensorDataToWindow(int sensor,
+                float[] values, int orientation) {
+            float x = values[0];
+            float y = values[1];
+            float z = values[2];
+
+            switch (sensor) {
+                case SensorManager.SENSOR_ORIENTATION:
+                case SensorManager.SENSOR_ORIENTATION_RAW:
+                    z = -z;
+                    break;
+                case SensorManager.SENSOR_ACCELEROMETER:
+                    x = -x;
+                    y = -y;
+                    z = -z;
+                    break;
+                case SensorManager.SENSOR_MAGNETIC_FIELD:
+                    x = -x;
+                    y = -y;
+                    break;
+            }
+            values[0] = x;
+            values[1] = y;
+            values[2] = z;
+            values[3] = x;
+            values[4] = y;
+            values[5] = z;
+
+            if ((orientation & Surface.ROTATION_90) != 0) {
+                // handles 90 and 270 rotation
+                switch (sensor) {
+                    case SensorManager.SENSOR_ACCELEROMETER:
+                    case SensorManager.SENSOR_MAGNETIC_FIELD:
+                        values[0] =-y;
+                        values[1] = x;
+                        values[2] = z;
+                        break;
+                    case SensorManager.SENSOR_ORIENTATION:
+                    case SensorManager.SENSOR_ORIENTATION_RAW:
+                        values[0] = x + ((x < 270) ? 90 : -270);
+                        values[1] = z;
+                        values[2] = y;
+                        break;
+                }
+            }
+            if ((orientation & Surface.ROTATION_180) != 0) {
+                x = values[0];
+                y = values[1];
+                z = values[2];
+                // handles 180 (flip) and 270 (flip + 90) rotation
+                switch (sensor) {
+                    case SensorManager.SENSOR_ACCELEROMETER:
+                    case SensorManager.SENSOR_MAGNETIC_FIELD:
+                        values[0] =-x;
+                        values[1] =-y;
+                        values[2] = z;
+                        break;
+                    case SensorManager.SENSOR_ORIENTATION:
+                    case SensorManager.SENSOR_ORIENTATION_RAW:
+                        values[0] = (x >= 180) ? (x - 180) : (x + 180);
+                        values[1] =-y;
+                        values[2] =-z;
+                        break;
+                }
+            }
+        }
+
+        private static int getLegacySensorType(int type) {
+            switch (type) {
+                case Sensor.TYPE_ACCELEROMETER:
+                    return SensorManager.SENSOR_ACCELEROMETER;
+                case Sensor.TYPE_MAGNETIC_FIELD:
+                    return SensorManager.SENSOR_MAGNETIC_FIELD;
+                case Sensor.TYPE_ORIENTATION:
+                    return SensorManager.SENSOR_ORIENTATION_RAW;
+                case Sensor.TYPE_TEMPERATURE:
+                    return SensorManager.SENSOR_TEMPERATURE;
+            }
+            return 0;
+        }
+    }
+
+    private static final class LmsFilter {
+        private static final int SENSORS_RATE_MS = 20;
+        private static final int COUNT = 12;
+        private static final float PREDICTION_RATIO = 1.0f/3.0f;
+        private static final float PREDICTION_TIME = (SENSORS_RATE_MS*COUNT/1000.0f)*PREDICTION_RATIO;
+        private float mV[] = new float[COUNT*2];
+        private float mT[] = new float[COUNT*2];
+        private int mIndex;
+
+        public LmsFilter() {
+            mIndex = COUNT;
+        }
+
+        public float filter(long time, float in) {
+            float v = in;
+            final float ns = 1.0f / 1000000000.0f;
+            final float t = time*ns;
+            float v1 = mV[mIndex];
+            if ((v-v1) > 180) {
+                v -= 360;
+            } else if ((v1-v) > 180) {
+                v += 360;
+            }
+            /* Manage the circular buffer, we write the data twice spaced
+             * by COUNT values, so that we don't have to copy the array
+             * when it's full
+             */
+            mIndex++;
+            if (mIndex >= COUNT*2)
+                mIndex = COUNT;
+            mV[mIndex] = v;
+            mT[mIndex] = t;
+            mV[mIndex-COUNT] = v;
+            mT[mIndex-COUNT] = t;
+
+            float A, B, C, D, E;
+            float a, b;
+            int i;
+
+            A = B = C = D = E = 0;
+            for (i=0 ; i<COUNT-1 ; i++) {
+                final int j = mIndex - 1 - i;
+                final float Z = mV[j];
+                final float T = 0.5f*(mT[j] + mT[j+1]) - t;
+                float dT = mT[j] - mT[j+1];
+                dT *= dT;
+                A += Z*dT;
+                B += T*(T*dT);
+                C +=   (T*dT);
+                D += Z*(T*dT);
+                E += dT;
+            }
+            b = (A*B + C*D) / (E*B + C*C);
+            a = (E*b - A) / C;
+            float f = b + PREDICTION_TIME*a;
+
+            // Normalize
+            f *= (1.0f / 360.0f);
+            if (((f>=0)?f:-f) >= 0.5f)
+                f = f - (float)Math.ceil(f + 0.5f) + 1.0f;
+            if (f < 0)
+                f += 1.0f;
+            f *= 360.0f;
+            return f;
+        }
+    }
+}