Sensor values changes.

1. Remove Sensor Event Pool.
2. Allocate fixed array values for each Sensor added.
   - Needed for backward compatibility.
3. Use SDK checks for backward compatibility for
   rotation vector.

b/8165631

Change-Id: I2d2cf10d3460478ed7af8e6188343f4bb033503c
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 19d495b..3fc82fa 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -449,7 +449,8 @@
 
         registerService(SENSOR_SERVICE, new ServiceFetcher() {
                 public Object createService(ContextImpl ctx) {
-                    return new SystemSensorManager(ctx.mMainThread.getHandler().getLooper());
+                    return new SystemSensorManager(ctx.getOuterContext(),
+                      ctx.mMainThread.getHandler().getLooper());
                 }});
 
         registerService(STATUS_BAR_SERVICE, new ServiceFetcher() {
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index af4c074..074f8fe 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -17,6 +17,8 @@
 
 package android.hardware;
 
+import android.os.Build;
+
 /**
  * Class representing a sensor. Use {@link SensorManager#getSensorList} to get
  * the list of available Sensors.
@@ -119,10 +121,12 @@
      * {@link android.hardware.SensorEvent#values SensorEvent.values} for more
      * details.
      * <p>
-     * No periodic calibration is performed (ie: there are no discontinuities
-     * in the data stream while using this sensor). Assumptions that the
-     * magnetic field is due to the Earth's poles is avoided. Factory calibration
-     * and temperature compensation is still performed.
+     * Similar to {@link #TYPE_MAGNETIC_FIELD} but the hard iron calibration (calibration
+     * due to distortions that arise from magnetized iron, steel or permanenet magnets
+     * on the device) is reported separately. No periodic calibration is performed
+     * (i.e. there are no discontinuities in the data stream while using this sensor).
+     * Assumptions that the magnetic field is due to the Earth's poles is avoided.
+     * Factory calibration and temperature compensation are still performed.
      * </p>
      */
     public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14;
@@ -193,11 +197,31 @@
             REPORTING_MODE_ON_CHANGE, REPORTING_MODE_CONTINUOUS, REPORTING_MODE_CONTINUOUS,
             REPORTING_MODE_CONTINUOUS, REPORTING_MODE_ONE_SHOT };
 
+    // Note: This needs to be updated, whenever a new sensor is added.
+    // Holds the maximum length of the values array associated with {@link SensorEvent} or
+    // {@link TriggerEvent} for the Sensor
+    private static int[] sMaxLengthValuesArray = {
+            3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 3, 3,
+            6, 4, 6, 1 };
+
     static int getReportingMode(Sensor sensor) {
         // mType starts from offset 1.
         return sSensorReportingModes[sensor.mType - 1];
     }
 
+    static int getMaxLengthValuesArray(Sensor sensor, int sdkLevel) {
+        // mType starts from offset 1.
+        int len = sMaxLengthValuesArray[sensor.mType - 1];
+
+        // RotationVector length has changed to 3 to 5 for API level 18
+        // Set it to 3 for backward compatibility.
+        if (sensor.getType() == Sensor.TYPE_ROTATION_VECTOR &&
+                sdkLevel <= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            len = 3;
+        }
+        return len;
+    }
+
     /* Some of these fields are set only by the native bindings in
      * SensorManager.
      */
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 84c9131..012ae7e 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -290,8 +290,12 @@
      * <li> values[0]: x*sin(&#952/2) </li>
      * <li> values[1]: y*sin(&#952/2) </li>
      * <li> values[2]: z*sin(&#952/2) </li>
-     * <li> values[3]: cos(&#952/2) <i>(optional: only if value.length = 4)</i> </li>
+     * <li> values[3]: cos(&#952/2) </li>
+     * <li> values[4]: estimated heading Accuracy (in radians) (-1 if unavailable)</li>
      * </ul>
+     * <p> values[3], originally optional, will always be present from SDK Level 18 onwards.
+     * values[4] is a new value that has been added in SDK Level 18.
+     * </p>
      *
      * <h4>{@link android.hardware.Sensor#TYPE_ORIENTATION
      * Sensor.TYPE_ORIENTATION}:</h4> All values are angles in degrees.
@@ -395,13 +399,50 @@
      * @see GeomagneticField
      *
      * <h4>{@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD_UNCALIBRATED} </h4>
-     * All values are in micro-Tesla (uT) and measure the ambient magnetic field
-     * in the X, Y and Z axis.
+     * Similar to {@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD},
+     * but the hard iron calibration is reported separately instead of being included
+     * in the measurement. Factory calibration and temperature compensation will still
+     * be applied to the "uncalibrated" measurement. Assumptions that the magnetic field
+     * is due to the Earth's poles is avoided.
      * <p>
-     * No periodic calibration is performed (ie: there are no discontinuities
-     * in the data stream while using this sensor). Assumptions that the the
-     * magnetic field is due to the Earth's poles is avoided. Factory calibration
-     * and temperature compensation is still performed.
+     * The values array is shown below:
+     * <ul>
+     * <li> values[0] = x_uncalib </li>
+     * <li> values[1] = y_uncalib </li>
+     * <li> values[2] = z_uncalib </li>
+     * <li> values[3] = x_bias </li>
+     * <li> values[4] = y_bias </li>
+     * <li> values[5] = z_bias </li>
+     * </ul>
+     * </p>
+     * <p>
+     * x_uncalib, y_uncalib, z_uncalib are the measured magnetic field in X, Y, Z axes.
+     * Soft iron and temperature calibrations are applied. But the hard iron
+     * calibration is not applied. The values are in micro-Tesla (uT).
+     * </p>
+     * <p>
+     * x_bias, y_bias, z_bias give the iron bias estimated in X, Y, Z axes.
+     * Each field is a component of the estimated hard iron calibration.
+     * The values are in micro-Tesla (uT).
+     * </p>
+     * <p> Hard iron - These distortions arise due to the magnetized iron, steel or permanenet
+     * magnets on the device.
+     * Soft iron - These distortions arise due to the interaction with the earth's magentic
+     * field.
+     * </p>
+     * <h4> {@link android.hardware.Sensor#TYPE_GAME_ROTATION_VECTOR} </h4>
+     * Identical to {@link android.hardware.Sensor#TYPE_ROTATION_VECTOR} except that it
+     * doesn't use the geomagnetic field. Therefore the Y axis doesn't
+     * point north, but instead to some other reference, that reference is
+     * allowed to drift by the same order of magnitude as the gyroscope
+     * drift around the Z axis.
+     * <p>
+     * In the ideal case, a phone rotated and returning to the same real-world
+     * orientation will report the same game rotation vector
+     * (without using the earth's geomagnetic field). However, the orientation
+     * may drift somewhat over time. See {@link android.hardware.Sensor#TYPE_ROTATION_VECTOR}
+     * for a detailed description of the values. This sensor will not have
+     * the estimated heading accuracy value.
      * </p>
      *
      * <h4> {@link android.hardware.Sensor#TYPE_GYROSCOPE_UNCALIBRATED} </h4>
@@ -452,7 +493,7 @@
      */
     public long timestamp;
 
-    SensorEvent(int size) {
-        values = new float[size];
+    SensorEvent(int valueSize) {
+        values = new float[valueSize];
     }
 }
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 3c231e5..c6c999b 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -1,4 +1,4 @@
-/*
+    /*
  * Copyright (C) 2012 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +16,10 @@
 
 package android.hardware;
 
+import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.MessageQueue;
-import android.util.Log;
-import android.util.Pools;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
@@ -51,16 +50,14 @@
     private final HashMap<TriggerEventListener, TriggerEventQueue> mTriggerListeners =
             new HashMap<TriggerEventListener, TriggerEventQueue>();
 
-    private static final int MAX_EVENTS = 16;
-    private static Pools.SynchronizedPool<SensorEvent> sSensorEventPool;
-    private static Pools.SynchronizedPool<TriggerEvent> sTriggerEventPool;
-
     // Looper associated with the context in which this instance was created.
     private final Looper mMainLooper;
+    private final int mTargetSdkLevel;
 
     /** {@hide} */
-    public SystemSensorManager(Looper mainLooper) {
+    public SystemSensorManager(Context context, Looper mainLooper) {
         mMainLooper = mainLooper;
+        mTargetSdkLevel = context.getApplicationInfo().targetSdkVersion;
         synchronized(sSensorModuleLock) {
             if (!sSensorModuleInitialized) {
                 sSensorModuleInitialized = true;
@@ -80,11 +77,6 @@
                         sHandleToSensor.append(sensor.getHandle(), sensor);
                     }
                 } while (i>0);
-
-                sSensorEventPool = new Pools.SynchronizedPool<SensorEvent>(
-                        sFullSensorsList.size()*2);
-                sTriggerEventPool = new Pools.SynchronizedPool<TriggerEvent>(
-                        sFullSensorsList.size()*2);
             }
         }
     }
@@ -115,7 +107,7 @@
             SensorEventQueue queue = mSensorListeners.get(listener);
             if (queue == null) {
                 Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
-                queue = new SensorEventQueue(listener, looper);
+                queue = new SensorEventQueue(listener, looper, this);
                 if (!queue.addSensor(sensor, delay)) {
                     queue.dispose();
                     return false;
@@ -208,6 +200,7 @@
      */
     private static abstract class BaseEventQueue {
         private native int nativeInitBaseEventQueue(BaseEventQueue eventQ, MessageQueue msgQ,
+
                 float[] scratch);
         private static native int nativeEnableSensor(int eventQ, int handle, int us);
         private static native int nativeDisableSensor(int eventQ, int handle);
@@ -218,10 +211,12 @@
         protected final SparseBooleanArray mFirstEvent = new SparseBooleanArray();
         private final CloseGuard mCloseGuard = CloseGuard.get();
         private final float[] mScratch = new float[16];
+        protected final SystemSensorManager mManager;
 
-        BaseEventQueue(Looper looper) {
+        BaseEventQueue(Looper looper, SystemSensorManager manager) {
             nSensorEventQueue = nativeInitBaseEventQueue(this, looper.getQueue(), mScratch);
             mCloseGuard.open("dispose");
+            mManager = manager;
         }
 
         public void dispose() {
@@ -234,6 +229,7 @@
 
             if (enableSensor(sensor, delay) == 0) {
                 mActiveSensors.put(sensor.getHandle(), true);
+                addSensorEvent(sensor);
                 return true;
             }
             return false;
@@ -247,6 +243,7 @@
                     if (sensor != null) {
                         disableSensor(sensor);
                         mActiveSensors.put(handle, false);
+                        removeSensorEvent(sensor);
                     } else {
                         // it should never happen -- just ignore.
                     }
@@ -260,6 +257,7 @@
             if (mActiveSensors.get(handle)) {
                 disableSensor(sensor);
                 mActiveSensors.put(sensor.getHandle(), false);
+                removeSensorEvent(sensor);
                 return true;
             }
             return false;
@@ -304,91 +302,104 @@
         }
         protected abstract void dispatchSensorEvent(int handle, float[] values, int accuracy,
                 long timestamp);
+
+        protected abstract void addSensorEvent(Sensor sensor);
+        protected abstract void removeSensorEvent(Sensor sensor);
     }
 
     static final class SensorEventQueue extends BaseEventQueue {
         private final SensorEventListener mListener;
+        private final SparseArray<SensorEvent> mSensorsEvents = new SparseArray<SensorEvent>();
 
-        public SensorEventQueue(SensorEventListener listener, Looper looper) {
-            super(looper);
+        public SensorEventQueue(SensorEventListener listener, Looper looper,
+                SystemSensorManager manager) {
+            super(looper, manager);
             mListener = listener;
         }
 
+        public void addSensorEvent(Sensor sensor) {
+            SensorEvent t = new SensorEvent(Sensor.getMaxLengthValuesArray(sensor,
+                    mManager.mTargetSdkLevel));
+            mSensorsEvents.put(sensor.getHandle(), t);
+        }
+
+        public void removeSensorEvent(Sensor sensor) {
+            mSensorsEvents.delete(sensor.getHandle());
+        }
+
         // Called from native code.
         @SuppressWarnings("unused")
         @Override
         protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy,
                 long timestamp) {
             final Sensor sensor = sHandleToSensor.get(handle);
-            SensorEvent t = sSensorEventPool.acquire();
-            if (t == null) t = new SensorEvent(MAX_EVENTS);
-            try {
-                // Copy the entire values array.
-                // Any changes in length will be handled at the native layer.
-                System.arraycopy(values, 0, t.values, 0, t.values.length);
-                t.timestamp = timestamp;
-                t.accuracy = inAccuracy;
-                t.sensor = sensor;
-                switch (t.sensor.getType()) {
-                    // Only report accuracy for sensors that support it.
-                    case Sensor.TYPE_MAGNETIC_FIELD:
-                    case Sensor.TYPE_ORIENTATION:
-                        // call onAccuracyChanged() only if the value changes
-                        final int accuracy = mSensorAccuracies.get(handle);
-                        if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
-                            mSensorAccuracies.put(handle, t.accuracy);
-                            mListener.onAccuracyChanged(t.sensor, t.accuracy);
-                        }
-                        break;
-                    default:
-                        // For other sensors, just report the accuracy once
-                        if (mFirstEvent.get(handle) == false) {
-                            mFirstEvent.put(handle, true);
-                            mListener.onAccuracyChanged(
-                                    t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
-                        }
-                        break;
-                }
-                mListener.onSensorChanged(t);
-            } finally {
-                sSensorEventPool.release(t);
+            SensorEvent t = mSensorsEvents.get(handle);
+            // Copy from the values array.
+            System.arraycopy(values, 0, t.values, 0, t.values.length);
+            t.timestamp = timestamp;
+            t.accuracy = inAccuracy;
+            t.sensor = sensor;
+            switch (t.sensor.getType()) {
+                // Only report accuracy for sensors that support it.
+                case Sensor.TYPE_MAGNETIC_FIELD:
+                case Sensor.TYPE_ORIENTATION:
+                    // call onAccuracyChanged() only if the value changes
+                    final int accuracy = mSensorAccuracies.get(handle);
+                    if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
+                        mSensorAccuracies.put(handle, t.accuracy);
+                        mListener.onAccuracyChanged(t.sensor, t.accuracy);
+                    }
+                    break;
+                default:
+                    // For other sensors, just report the accuracy once
+                    if (mFirstEvent.get(handle) == false) {
+                        mFirstEvent.put(handle, true);
+                        mListener.onAccuracyChanged(
+                                t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
+                    }
+                    break;
             }
+            mListener.onSensorChanged(t);
         }
     }
 
     static final class TriggerEventQueue extends BaseEventQueue {
         private final TriggerEventListener mListener;
-        private SensorManager mManager;
+        private final SparseArray<TriggerEvent> mTriggerEvents = new SparseArray<TriggerEvent>();
 
         public TriggerEventQueue(TriggerEventListener listener, Looper looper,
-                SensorManager manager) {
-            super(looper);
+                SystemSensorManager manager) {
+            super(looper, manager);
             mListener = listener;
-            mManager = manager;
+        }
+
+        public void addSensorEvent(Sensor sensor) {
+            TriggerEvent t = new TriggerEvent(Sensor.getMaxLengthValuesArray(sensor,
+                    mManager.mTargetSdkLevel));
+            mTriggerEvents.put(sensor.getHandle(), t);
+        }
+
+        public void removeSensorEvent(Sensor sensor) {
+            mTriggerEvents.delete(sensor.getHandle());
         }
 
         // Called from native code.
         @SuppressWarnings("unused")
         @Override
-        protected void dispatchSensorEvent(int handle, float[] values, int accuracy, long timestamp) {
+        protected void dispatchSensorEvent(int handle, float[] values, int accuracy,
+                long timestamp) {
             final Sensor sensor = sHandleToSensor.get(handle);
-            TriggerEvent t = sTriggerEventPool.acquire();
-            if (t == null) t = new TriggerEvent(MAX_EVENTS);
+            TriggerEvent t = mTriggerEvents.get(handle);
 
-            try {
-                // Copy the entire values array.
-                // Any changes in length will be handled at the native layer.
-                System.arraycopy(values, 0, t.values, 0, t.values.length);
-                t.timestamp = timestamp;
-                t.sensor = sensor;
+            // Copy from the values array.
+            System.arraycopy(values, 0, t.values, 0, t.values.length);
+            t.timestamp = timestamp;
+            t.sensor = sensor;
 
-                // A trigger sensor should be auto disabled.
-                mManager.cancelTriggerSensorImpl(mListener, sensor);
+            // A trigger sensor should be auto disabled.
+            mManager.cancelTriggerSensorImpl(mListener, sensor);
 
-                mListener.onTrigger(t);
-            } finally {
-                sTriggerEventPool.release(t);
-            }
+            mListener.onTrigger(t);
         }
     }
 }
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index 2652739..1203e02 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -432,7 +432,7 @@
             mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting();
             mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting();
 
-            SensorManager sensorManager = new SystemSensorManager(mHandler.getLooper());
+            SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());
 
             // The notifier runs on the system server's main looper so as not to interfere
             // with the animations and other critical functions of the power manager.