Small tweaks to orientation.

Improved threshold for detecting external acceleration.

Bug: 5976859
Change-Id: Iaf2298fba8eda72d1cacbb2f3aea72f460a9262f
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
index c28b220..4c34dd4 100755
--- a/core/java/android/view/WindowOrientationListener.java
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -21,6 +21,7 @@
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.os.SystemProperties;
 import android.util.FloatMath;
 import android.util.Log;
 import android.util.Slog;
@@ -34,20 +35,15 @@
  * "App/Activity/Screen Orientation" to ensure that all orientation
  * modes still work correctly.
  *
- * You can also visualize the behavior of the WindowOrientationListener by
- * enabling the window orientation listener log using the Development Settings
- * in the Dev Tools application (Development.apk)
- * and running frameworks/base/tools/orientationplot/orientationplot.py.
- *
- * More information about how to tune this algorithm in
- * frameworks/base/tools/orientationplot/README.txt.
+ * You can also visualize the behavior of the WindowOrientationListener.
+ * Refer to frameworks/base/tools/orientationplot/README.txt for details.
  *
  * @hide
  */
 public abstract class WindowOrientationListener {
     private static final String TAG = "WindowOrientationListener";
-    private static final boolean DEBUG = false;
-    private static final boolean localLOGV = DEBUG || false;
+    private static final boolean LOG = SystemProperties.getBoolean(
+            "debug.orientation.log", false);
 
     private static final boolean USE_GRAVITY_SENSOR = false;
 
@@ -56,7 +52,6 @@
     private int mRate;
     private Sensor mSensor;
     private SensorEventListenerImpl mSensorEventListener;
-    boolean mLogEnabled;
     int mCurrentRotation = -1;
 
     /**
@@ -100,7 +95,9 @@
             return;
         }
         if (mEnabled == false) {
-            if (localLOGV) Log.d(TAG, "WindowOrientationListener enabled");
+            if (LOG) {
+                Log.d(TAG, "WindowOrientationListener enabled");
+            }
             mSensorManager.registerListener(mSensorEventListener, mSensor, mRate);
             mEnabled = true;
         }
@@ -115,7 +112,9 @@
             return;
         }
         if (mEnabled == true) {
-            if (localLOGV) Log.d(TAG, "WindowOrientationListener disabled");
+            if (LOG) {
+                Log.d(TAG, "WindowOrientationListener disabled");
+            }
             mSensorManager.unregisterListener(mSensorEventListener);
             mEnabled = false;
         }
@@ -165,16 +164,6 @@
     public abstract void onProposedRotationChanged(int rotation);
 
     /**
-     * Enables or disables the window orientation listener logging for use with
-     * the orientationplot.py tool.
-     * Logging is usually enabled via Development Settings.  (See class comments.)
-     * @param enable True to enable logging.
-     */
-    public void setLogEnabled(boolean enable) {
-        mLogEnabled = enable;
-    }
-
-    /**
      * This class filters the raw accelerometer data and tries to detect actual changes in
      * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters,
      * but here's the outline:
@@ -238,11 +227,16 @@
         // can change.
         private static final long PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS = 500 * NANOS_PER_MS;
 
-        // The mininum amount of time that must have elapsed since the device stopped
+        // The minimum amount of time that must have elapsed since the device stopped
         // swinging (time since device appeared to be in the process of being put down
         // or put away into a pocket) before the proposed rotation can change.
         private static final long PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS = 300 * NANOS_PER_MS;
 
+        // The minimum amount of time that must have elapsed since the device stopped
+        // undergoing external acceleration before the proposed rotation can change.
+        private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS =
+                500 * NANOS_PER_MS;
+
         // If the tilt angle remains greater than the specified angle for a minimum of
         // the specified time, then the device is deemed to be lying flat
         // (just chillin' on a table).
@@ -300,10 +294,15 @@
         // singularities in the tilt and orientation calculations.
         //
         // In both cases, we postpone choosing an orientation.
+        //
+        // However, we need to tolerate some acceleration because the angular momentum
+        // of turning the device can skew the observed acceleration for a short period of time.
+        private static final float NEAR_ZERO_MAGNITUDE = 1; // m/s^2
+        private static final float ACCELERATION_TOLERANCE = 4; // m/s^2
         private static final float MIN_ACCELERATION_MAGNITUDE =
-                SensorManager.STANDARD_GRAVITY * 0.3f;
+                SensorManager.STANDARD_GRAVITY - ACCELERATION_TOLERANCE;
         private static final float MAX_ACCELERATION_MAGNITUDE =
-            SensorManager.STANDARD_GRAVITY * 1.25f;
+            SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE;
 
         // Maximum absolute tilt angle at which to consider orientation data.  Beyond this (i.e.
         // when screen is facing the sky or ground), we completely ignore orientation data.
@@ -353,6 +352,9 @@
         // Timestamp when the device last appeared to be swinging.
         private long mSwingTimestampNanos;
 
+        // Timestamp when the device last appeared to be undergoing external acceleration.
+        private long mAccelerationTimestampNanos;
+
         // History of observed tilt angles.
         private static final int TILT_HISTORY_SIZE = 40;
         private float[] mTiltHistory = new float[TILT_HISTORY_SIZE];
@@ -374,15 +376,13 @@
 
         @Override
         public void onSensorChanged(SensorEvent event) {
-            final boolean log = mOrientationListener.mLogEnabled;
-
             // The vector given in the SensorEvent points straight up (towards the sky) under ideal
             // conditions (the phone is not accelerating).  I'll call this up vector elsewhere.
             float x = event.values[ACCELEROMETER_DATA_X];
             float y = event.values[ACCELEROMETER_DATA_Y];
             float z = event.values[ACCELEROMETER_DATA_Z];
 
-            if (log) {
+            if (LOG) {
                 Slog.v(TAG, "Raw acceleration vector: "
                         + "x=" + x + ", y=" + y + ", z=" + z
                         + ", magnitude=" + FloatMath.sqrt(x * x + y * y + z * z));
@@ -399,7 +399,7 @@
             if (now < then
                     || now > then + MAX_FILTER_DELTA_TIME_NANOS
                     || (x == 0 && y == 0 && z == 0)) {
-                if (log) {
+                if (LOG) {
                     Slog.v(TAG, "Resetting orientation listener.");
                 }
                 reset();
@@ -409,7 +409,7 @@
                 x = alpha * (x - mLastFilteredX) + mLastFilteredX;
                 y = alpha * (y - mLastFilteredY) + mLastFilteredY;
                 z = alpha * (z - mLastFilteredZ) + mLastFilteredZ;
-                if (log) {
+                if (LOG) {
                     Slog.v(TAG, "Filtered acceleration vector: "
                             + "x=" + x + ", y=" + y + ", z=" + z
                             + ", magnitude=" + FloatMath.sqrt(x * x + y * y + z * z));
@@ -421,18 +421,24 @@
             mLastFilteredY = y;
             mLastFilteredZ = z;
 
+            boolean isAccelerating = false;
             boolean isFlat = false;
             boolean isSwinging = false;
             if (!skipSample) {
                 // Calculate the magnitude of the acceleration vector.
                 final float magnitude = FloatMath.sqrt(x * x + y * y + z * z);
-                if (magnitude < MIN_ACCELERATION_MAGNITUDE
-                        || magnitude > MAX_ACCELERATION_MAGNITUDE) {
-                    if (log) {
-                        Slog.v(TAG, "Ignoring sensor data, magnitude out of range.");
+                if (magnitude < NEAR_ZERO_MAGNITUDE) {
+                    if (LOG) {
+                        Slog.v(TAG, "Ignoring sensor data, magnitude too close to zero.");
                     }
                     clearPredictedRotation();
                 } else {
+                    // Determine whether the device appears to be undergoing external acceleration.
+                    if (isAccelerating(magnitude)) {
+                        isAccelerating = true;
+                        mAccelerationTimestampNanos = now;
+                    }
+
                     // Calculate the tilt angle.
                     // This is the angle between the up vector and the x-y plane (the plane of
                     // the screen) in a range of [-90, 90] degrees.
@@ -441,6 +447,7 @@
                     //    90 degrees: screen horizontal and facing the sky (on table)
                     final int tiltAngle = (int) Math.round(
                             Math.asin(z / magnitude) * RADIANS_TO_DEGREES);
+                    addTiltHistoryEntry(now, tiltAngle);
 
                     // Determine whether the device appears to be flat or swinging.
                     if (isFlat(now)) {
@@ -451,12 +458,11 @@
                         isSwinging = true;
                         mSwingTimestampNanos = now;
                     }
-                    addTiltHistoryEntry(now, tiltAngle);
 
                     // If the tilt angle is too close to horizontal then we cannot determine
                     // the orientation angle of the screen.
                     if (Math.abs(tiltAngle) > MAX_TILT) {
-                        if (log) {
+                        if (LOG) {
                             Slog.v(TAG, "Ignoring sensor data, tilt angle too high: "
                                     + "tiltAngle=" + tiltAngle);
                         }
@@ -483,7 +489,7 @@
                                 && isOrientationAngleAcceptable(nearestRotation,
                                         orientationAngle)) {
                             updatePredictedRotation(now, nearestRotation);
-                            if (log) {
+                            if (LOG) {
                                 Slog.v(TAG, "Predicted: "
                                         + "tiltAngle=" + tiltAngle
                                         + ", orientationAngle=" + orientationAngle
@@ -493,7 +499,7 @@
                                                         * 0.000001f));
                             }
                         } else {
-                            if (log) {
+                            if (LOG) {
                                 Slog.v(TAG, "Ignoring sensor data, no predicted rotation: "
                                         + "tiltAngle=" + tiltAngle
                                         + ", orientationAngle=" + orientationAngle);
@@ -511,15 +517,18 @@
             }
 
             // Write final statistics about where we are in the orientation detection process.
-            if (log) {
+            if (LOG) {
                 Slog.v(TAG, "Result: currentRotation=" + mOrientationListener.mCurrentRotation
                         + ", proposedRotation=" + mProposedRotation
                         + ", predictedRotation=" + mPredictedRotation
                         + ", timeDeltaMS=" + timeDeltaMS
+                        + ", isAccelerating=" + isAccelerating
                         + ", isFlat=" + isFlat
                         + ", isSwinging=" + isSwinging
                         + ", timeUntilSettledMS=" + remainingMS(now,
                                 mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS)
+                        + ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now,
+                                mAccelerationTimestampNanos + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS)
                         + ", timeUntilFlatDelayExpiredMS=" + remainingMS(now,
                                 mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS)
                         + ", timeUntilSwingDelayExpiredMS=" + remainingMS(now,
@@ -528,7 +537,7 @@
 
             // Tell the listener.
             if (mProposedRotation != oldProposedRotation && mProposedRotation >= 0) {
-                if (log) {
+                if (LOG) {
                     Slog.v(TAG, "Proposed rotation changed!  proposedRotation=" + mProposedRotation
                             + ", oldProposedRotation=" + oldProposedRotation);
                 }
@@ -618,6 +627,12 @@
                 return false;
             }
 
+            // The last acceleration state must have been sufficiently long ago.
+            if (now < mAccelerationTimestampNanos
+                    + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) {
+                return false;
+            }
+
             // Looks good!
             return true;
         }
@@ -627,6 +642,7 @@
             mProposedRotation = -1;
             mFlatTimestampNanos = Long.MIN_VALUE;
             mSwingTimestampNanos = Long.MIN_VALUE;
+            mAccelerationTimestampNanos = Long.MIN_VALUE;
             clearPredictedRotation();
             clearTiltHistory();
         }
@@ -643,6 +659,11 @@
             }
         }
 
+        private boolean isAccelerating(float magnitude) {
+            return magnitude < MIN_ACCELERATION_MAGNITUDE
+                    || magnitude > MAX_ACCELERATION_MAGNITUDE;
+        }
+
         private void clearTiltHistory() {
             mTiltHistoryTimestampNanos[0] = Long.MIN_VALUE;
             mTiltHistoryIndex = 1;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index b3ca171..6348d37 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -553,8 +553,6 @@
             resolver.registerContentObserver(Settings.System.getUriFor(
                     Settings.System.SCREEN_OFF_TIMEOUT), false, this);
             resolver.registerContentObserver(Settings.System.getUriFor(
-                    Settings.System.WINDOW_ORIENTATION_LISTENER_LOG), false, this);
-            resolver.registerContentObserver(Settings.System.getUriFor(
                     Settings.System.POINTER_LOCATION), false, this);
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
@@ -1098,10 +1096,6 @@
                 updateOrientationListenerLp();
             }
 
-            mOrientationListener.setLogEnabled(
-                    Settings.System.getInt(resolver,
-                            Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, 0) != 0);
-
             if (mSystemReady) {
                 int pointerLocation = Settings.System.getInt(resolver,
                         Settings.System.POINTER_LOCATION, 0);
diff --git a/tools/orientationplot/README.txt b/tools/orientationplot/README.txt
index 0143510..d53f65e 100644
--- a/tools/orientationplot/README.txt
+++ b/tools/orientationplot/README.txt
@@ -16,15 +16,15 @@
 The tool works by scaping the debug log output from WindowOrientationListener
 for interesting data and then plotting it.
 
-1. Enable the Window Orientation Listener debugging data log using the
-   Development Settings in the Dev Tools application (Development.apk).
-
-2. Plug in the device.  Ensure that it is the only device plugged in
+1. Plug in the device.  Ensure that it is the only device plugged in
    since this script is of very little brain and will get confused otherwise.
 
-3. Run "orientationplot.py".
+2. Enable the Window Orientation Listener debugging data log.
+   adb shell setprop debug.orientation.log true
+   adb shell stop
+   adb shell start
 
-4. When finished, remember to disable the debug log output since it is quite verbose!
+3. Run "orientationplot.py".
 
 
 WHAT IT ALL MEANS
diff --git a/tools/orientationplot/orientationplot.py b/tools/orientationplot/orientationplot.py
index f4e6b45..6fc3922 100755
--- a/tools/orientationplot/orientationplot.py
+++ b/tools/orientationplot/orientationplot.py
@@ -152,6 +152,7 @@
     self.time_until_settled = self._make_timeseries()
     self.time_until_flat_delay_expired = self._make_timeseries()
     self.time_until_swing_delay_expired = self._make_timeseries()
+    self.time_until_acceleration_delay_expired = self._make_timeseries()
     self.stability_axes = self._add_timeseries_axes(
         6, 'Proposal Stability', 'ms', [-10, 600],
         sharex=shared_axis,
@@ -162,6 +163,8 @@
         self.stability_axes, 'time until flat delay expired', 'green')
     self.time_until_swing_delay_expired_line = self._add_timeseries_line(
         self.stability_axes, 'time until swing delay expired', 'blue')
+    self.time_until_acceleration_delay_expired_line = self._add_timeseries_line(
+        self.stability_axes, 'time until acceleration delay expired', 'red')
     self._add_timeseries_legend(self.stability_axes)
 
     self.sample_latency = self._make_timeseries()
@@ -253,6 +256,7 @@
     self.parse_time_until_settled = None
     self.parse_time_until_flat_delay_expired = None
     self.parse_time_until_swing_delay_expired = None
+    self.parse_time_until_acceleration_delay_expired = None
     self.parse_sample_latency = None
 
   # Update samples.
@@ -303,6 +307,7 @@
         self.parse_time_until_settled = self._get_following_number(line, 'timeUntilSettledMS=')
         self.parse_time_until_flat_delay_expired = self._get_following_number(line, 'timeUntilFlatDelayExpiredMS=')
         self.parse_time_until_swing_delay_expired = self._get_following_number(line, 'timeUntilSwingDelayExpiredMS=')
+        self.parse_time_until_acceleration_delay_expired = self._get_following_number(line, 'timeUntilAccelerationDelayExpiredMS=')
 
         self._append(self.raw_acceleration_x, timeindex, self.parse_raw_acceleration_x)
         self._append(self.raw_acceleration_y, timeindex, self.parse_raw_acceleration_y)
@@ -326,6 +331,7 @@
         self._append(self.time_until_settled, timeindex, self.parse_time_until_settled)
         self._append(self.time_until_flat_delay_expired, timeindex, self.parse_time_until_flat_delay_expired)
         self._append(self.time_until_swing_delay_expired, timeindex, self.parse_time_until_swing_delay_expired)
+        self._append(self.time_until_acceleration_delay_expired, timeindex, self.parse_time_until_acceleration_delay_expired)
         self._append(self.sample_latency, timeindex, self.parse_sample_latency)
         self._reset_parse_state()
 
@@ -349,6 +355,7 @@
       self._scroll(self.time_until_settled, bottom)
       self._scroll(self.time_until_flat_delay_expired, bottom)
       self._scroll(self.time_until_swing_delay_expired, bottom)
+      self._scroll(self.time_until_acceleration_delay_expired, bottom)
       self._scroll(self.sample_latency, bottom)
 
     # Redraw the plots.
@@ -368,6 +375,7 @@
     self.time_until_settled_line.set_data(self.time_until_settled)
     self.time_until_flat_delay_expired_line.set_data(self.time_until_flat_delay_expired)
     self.time_until_swing_delay_expired_line.set_data(self.time_until_swing_delay_expired)
+    self.time_until_acceleration_delay_expired_line.set_data(self.time_until_acceleration_delay_expired)
     self.sample_latency_line.set_data(self.sample_latency)
 
     self.fig.canvas.draw_idle()