summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/WindowManagerPolicy.java7
-rwxr-xr-xcore/java/android/view/WindowOrientationListener.java457
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java28
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java1
-rwxr-xr-xtools/orientationplot/orientationplot.py132
5 files changed, 304 insertions, 321 deletions
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 1dbb083..779a7b3 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -876,6 +876,13 @@ public interface WindowManagerPolicy {
public boolean rotationHasCompatibleMetricsLw(int orientation, int rotation);
/**
+ * Called by the window manager when the rotation changes.
+ *
+ * @param rotation The new rotation.
+ */
+ public void setRotationLw(int rotation);
+
+ /**
* Called when the system is mostly done booting to determine whether
* the system should go into safe mode.
*/
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
index 726bf4a..c3c74a7 100755
--- a/core/java/android/view/WindowOrientationListener.java
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -26,7 +26,7 @@ import android.util.Slog;
/**
* A special helper class used by the WindowManager
- * for receiving notifications from the SensorManager when
+ * for receiving notifications from the SensorManager when
* the orientation of the device has changed.
*
* NOTE: If changing anything here, please run the API demo
@@ -54,6 +54,7 @@ public abstract class WindowOrientationListener {
private Sensor mSensor;
private SensorEventListenerImpl mSensorEventListener;
boolean mLogEnabled;
+ int mCurrentRotation = -1;
/**
* Creates a new WindowOrientationListener.
@@ -117,12 +118,25 @@ public abstract class WindowOrientationListener {
}
/**
- * Gets the current orientation.
- * @return The current rotation, or -1 if unknown.
+ * Sets the current rotation.
+ *
+ * @param rotation The current rotation.
+ */
+ public void setCurrentRotation(int rotation) {
+ mCurrentRotation = rotation;
+ }
+
+ /**
+ * Gets the proposed rotation.
+ *
+ * This method only returns a rotation if the orientation listener is certain
+ * of its proposal. If the rotation is indeterminate, returns -1.
+ *
+ * @return The proposed rotation, or -1 if unknown.
*/
- public int getCurrentRotation() {
+ public int getProposedRotation() {
if (mEnabled) {
- return mSensorEventListener.getCurrentRotation();
+ return mSensorEventListener.getProposedRotation();
}
return -1;
}
@@ -137,10 +151,14 @@ public abstract class WindowOrientationListener {
/**
* Called when the rotation view of the device has changed.
*
+ * This method is called whenever the orientation becomes certain of an orientation.
+ * It is called each time the orientation determination transitions from being
+ * uncertain to being certain again, even if it is the same orientation as before.
+ *
* @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants.
* @see Surface
*/
- public abstract void onOrientationChanged(int rotation);
+ public abstract void onProposedRotationChanged(int rotation);
/**
* Enables or disables the window orientation listener logging for use with
@@ -182,23 +200,8 @@ public abstract class WindowOrientationListener {
* to the corresponding orientation. These thresholds have some hysteresis built-in
* to avoid oscillations between adjacent orientations.
*
- * - Use the magnitude to judge the confidence of the orientation.
- * Under ideal conditions, the magnitude should equal to that of gravity. When it
- * differs significantly, we know the device is under external acceleration and
- * we can't trust the data.
- *
- * - Use the tilt angle to judge the confidence of the orientation.
- * When the tilt angle is high in absolute value then the device is nearly flat
- * so small physical movements produce large changes in orientation angle.
- * This can be the case when the device is being picked up from a table.
- *
- * - Use the orientation angle to judge the confidence of the orientation.
- * The close the orientation angle is to the canonical orientation angle, the better.
- *
- * - Based on the aggregate confidence, we determine how long we want to wait for
- * the new orientation to settle. This is accomplished by integrating the confidence
- * for each orientation over time. When a threshold integration sum is reached
- * then we actually change orientations.
+ * - Wait for the device to settle for a little bit. Once that happens, issue the
+ * new orientation proposal.
*
* Details are explained inline.
*/
@@ -211,22 +214,8 @@ public abstract class WindowOrientationListener {
private static final int ACCELEROMETER_DATA_Y = 1;
private static final int ACCELEROMETER_DATA_Z = 2;
- // Rotation constants.
- // These are the same as Surface rotation constants with the addition of a 5th
- // unknown state when we are not confident about the proporsed orientation.
- // One important property of these constants is that they are equal to the
- // orientation angle itself divided by 90. We use this fact to map
- // back and forth between orientation angles and rotation values.
- private static final int ROTATION_UNKNOWN = -1;
- //private static final int ROTATION_0 = Surface.ROTATION_0; // 0
- //private static final int ROTATION_90 = Surface.ROTATION_90; // 1
- //private static final int ROTATION_180 = Surface.ROTATION_180; // 2
- //private static final int ROTATION_270 = Surface.ROTATION_270; // 3
-
private final WindowOrientationListener mOrientationListener;
- private int mRotation = ROTATION_UNKNOWN;
-
/* State for first order low-pass filtering of accelerometer data.
* See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for
* signal processing background.
@@ -235,6 +224,24 @@ public abstract class WindowOrientationListener {
private long mLastTimestamp = Long.MAX_VALUE; // in nanoseconds
private float mLastFilteredX, mLastFilteredY, mLastFilteredZ;
+ // The current proposal. We wait for the proposal to be stable for a
+ // certain amount of time before accepting it.
+ //
+ // The basic idea is to ignore intermediate poses of the device while the
+ // user is picking up, putting down or turning the device.
+ private int mProposalRotation;
+ private long mProposalAgeMS;
+
+ // A historical trace of tilt and orientation angles. Used to determine whether
+ // the device posture has settled down.
+ private static final int HISTORY_SIZE = 20;
+ private int mHistoryIndex; // index of most recent sample
+ private int mHistoryLength; // length of historical trace
+ private final long[] mHistoryTimestampMS = new long[HISTORY_SIZE];
+ private final float[] mHistoryMagnitudes = new float[HISTORY_SIZE];
+ private final int[] mHistoryTiltAngles = new int[HISTORY_SIZE];
+ private final int[] mHistoryOrientationAngles = new int[HISTORY_SIZE];
+
// The maximum sample inter-arrival time in milliseconds.
// If the acceleration samples are further apart than this amount in time, we reset the
// state of the low-pass filter and orientation properties. This helps to handle
@@ -242,24 +249,26 @@ public abstract class WindowOrientationListener {
// a significant gap in samples.
private static final float MAX_FILTER_DELTA_TIME_MS = 1000;
- // The acceleration filter cutoff frequency.
- // This is the frequency at which signals are attenuated by 3dB (half the passband power).
+ // The acceleration filter time constant.
+ //
+ // This time constant is used to tune the acceleration filter such that
+ // impulses and vibrational noise (think car dock) is suppressed before we
+ // try to calculate the tilt and orientation angles.
+ //
+ // The filter time constant is related to the filter cutoff frequency, which is the
+ // frequency at which signals are attenuated by 3dB (half the passband power).
// Each successive octave beyond this frequency is attenuated by an additional 6dB.
//
- // We choose the cutoff frequency such that impulses and vibrational noise
- // (think car dock) is suppressed. However, this filtering does not eliminate
- // all possible sources of orientation ambiguity so we also rely on a dynamic
- // settle time for establishing a new orientation. Filtering adds latency
- // inversely proportional to the cutoff frequency so we don't want to make
- // it too small or we can lose hundreds of milliseconds of responsiveness.
- private static final float FILTER_CUTOFF_FREQUENCY_HZ = 1f;
- private static final float FILTER_TIME_CONSTANT_MS = (float)(500.0f
- / (Math.PI * FILTER_CUTOFF_FREQUENCY_HZ)); // t = 1 / (2pi * Fc) * 1000ms
-
- // The filter gain.
- // We choose a value slightly less than unity to avoid numerical instabilities due
- // to floating-point error accumulation.
- private static final float FILTER_GAIN = 0.999f;
+ // Given a time constant t in seconds, the filter cutoff frequency Fc in Hertz
+ // is given by Fc = 1 / (2pi * t).
+ //
+ // The higher the time constant, the lower the cutoff frequency, so more noise
+ // will be suppressed.
+ //
+ // Filtering adds latency proportional the time constant (inversely proportional
+ // to the cutoff frequency) so we don't want to make the time constant too
+ // large or we can lose responsiveness.
+ private static final float FILTER_TIME_CONSTANT_MS = 100.0f;
/* State for orientation detection. */
@@ -297,10 +306,10 @@ public abstract class WindowOrientationListener {
// The ideal tilt angle is 0 (when the device is vertical) so the limits establish
// how close to vertical the device must be in order to change orientation.
private static final int[][] TILT_TOLERANCE = new int[][] {
- /* ROTATION_0 */ { -20, 75 },
- /* ROTATION_90 */ { -20, 70 },
- /* ROTATION_180 */ { -20, 65 },
- /* ROTATION_270 */ { -20, 70 }
+ /* ROTATION_0 */ { -20, 70 },
+ /* ROTATION_90 */ { -20, 60 },
+ /* ROTATION_180 */ { -20, 50 },
+ /* ROTATION_270 */ { -20, 60 }
};
// The gap angle in degrees between adjacent orientation angles for hysteresis.
@@ -308,41 +317,31 @@ public abstract class WindowOrientationListener {
// adjacent orientation. No orientation proposal is made when the orientation
// angle is within the gap between the current orientation and the adjacent
// orientation.
- private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 30;
-
- // The confidence scale factors for angle, tilt and magnitude.
- // When the distance between the actual value and the ideal value is the
- // specified delta, orientation transitions will take twice as long as they would
- // in the ideal case. Increasing or decreasing the delta has an exponential effect
- // on each factor's influence over the transition time.
+ private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 45;
- // Transition takes 2x longer when angle is 30 degrees from ideal orientation angle.
- private static final float ORIENTATION_ANGLE_CONFIDENCE_SCALE =
- confidenceScaleFromDelta(30);
+ // The number of milliseconds for which the device posture must be stable
+ // before we perform an orientation change. If the device appears to be rotating
+ // (being picked up, put down) then we keep waiting until it settles.
+ private static final int SETTLE_TIME_MS = 200;
- // Transition takes 2x longer when tilt is 60 degrees from vertical.
- private static final float TILT_ANGLE_CONFIDENCE_SCALE = confidenceScaleFromDelta(60);
+ // The maximum change in magnitude that can occur during the settle time.
+ // Tuning this constant particularly helps to filter out situations where the
+ // device is being picked up or put down by the user.
+ private static final float SETTLE_MAGNITUDE_MAX_DELTA =
+ SensorManager.STANDARD_GRAVITY * 0.2f;
- // Transition takes 2x longer when acceleration is 0.5 Gs.
- private static final float MAGNITUDE_CONFIDENCE_SCALE = confidenceScaleFromDelta(
- SensorManager.STANDARD_GRAVITY * 0.5f);
+ // The maximum change in tilt angle that can occur during the settle time.
+ private static final int SETTLE_TILT_ANGLE_MAX_DELTA = 5;
- // The number of milliseconds for which a new orientation must be stable before
- // we perform an orientation change under ideal conditions. It will take
- // proportionally longer than this to effect an orientation change when
- // the proposed orientation confidence is low.
- private static final float ORIENTATION_SETTLE_TIME_MS = 250;
-
- // The confidence that we have abount effecting each orientation change.
- // When one of these values exceeds 1.0, we have determined our new orientation!
- private float mConfidence[] = new float[4];
+ // The maximum change in orientation angle that can occur during the settle time.
+ private static final int SETTLE_ORIENTATION_ANGLE_MAX_DELTA = 5;
public SensorEventListenerImpl(WindowOrientationListener orientationListener) {
mOrientationListener = orientationListener;
}
- public int getCurrentRotation() {
- return mRotation; // may be -1, if unknown
+ public int getProposedRotation() {
+ return mProposalAgeMS >= SETTLE_TIME_MS ? mProposalRotation : -1;
}
@Override
@@ -368,20 +367,18 @@ public abstract class WindowOrientationListener {
// Reset the orientation listener state if the samples are too far apart in time
// or when we see values of (0, 0, 0) which indicates that we polled the
// accelerometer too soon after turning it on and we don't have any data yet.
- final float timeDeltaMS = (event.timestamp - mLastTimestamp) * 0.000001f;
+ final long now = event.timestamp;
+ final float timeDeltaMS = (now - mLastTimestamp) * 0.000001f;
boolean skipSample;
if (timeDeltaMS <= 0 || timeDeltaMS > MAX_FILTER_DELTA_TIME_MS
|| (x == 0 && y == 0 && z == 0)) {
if (log) {
Slog.v(TAG, "Resetting orientation listener.");
}
- for (int i = 0; i < 4; i++) {
- mConfidence[i] = 0;
- }
+ clearProposal();
skipSample = true;
} else {
- final float alpha = timeDeltaMS
- / (FILTER_TIME_CONSTANT_MS + timeDeltaMS) * FILTER_GAIN;
+ final float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS);
x = alpha * (x - mLastFilteredX) + mLastFilteredX;
y = alpha * (y - mLastFilteredY) + mLastFilteredY;
z = alpha * (z - mLastFilteredZ) + mLastFilteredZ;
@@ -391,17 +388,13 @@ public abstract class WindowOrientationListener {
}
skipSample = false;
}
- mLastTimestamp = event.timestamp;
+ mLastTimestamp = now;
mLastFilteredX = x;
mLastFilteredY = y;
mLastFilteredZ = z;
- boolean orientationChanged = false;
+ final int oldProposedRotation = getProposedRotation();
if (!skipSample) {
- // Determine a proposed orientation based on the currently available data.
- int proposedOrientation = ROTATION_UNKNOWN;
- float combinedConfidence = 1.0f;
-
// Calculate the magnitude of the acceleration vector.
final float magnitude = (float) Math.sqrt(x * x + y * y + z * z);
if (magnitude < MIN_ACCELERATION_MAGNITUDE
@@ -410,6 +403,7 @@ public abstract class WindowOrientationListener {
Slog.v(TAG, "Ignoring sensor data, magnitude out of range: "
+ "magnitude=" + magnitude);
}
+ clearProposal();
} else {
// Calculate the tilt angle.
// This is the angle between the up vector and the x-y plane (the plane of
@@ -417,123 +411,82 @@ public abstract class WindowOrientationListener {
// -90 degrees: screen horizontal and facing the ground (overhead)
// 0 degrees: screen vertical
// 90 degrees: screen horizontal and facing the sky (on table)
- final int tiltAngle = (int) Math.round(
- Math.asin(z / magnitude) * RADIANS_TO_DEGREES);
-
- // 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) {
- Slog.v(TAG, "Ignoring sensor data, tilt angle too high: "
- + "magnitude=" + magnitude + ", tiltAngle=" + tiltAngle);
- }
- } else {
- // Calculate the orientation angle.
- // This is the angle between the x-y projection of the up vector onto
- // the +y-axis, increasing clockwise in a range of [0, 360] degrees.
- int orientationAngle = (int) Math.round(
- -Math.atan2(-x, y) * RADIANS_TO_DEGREES);
- if (orientationAngle < 0) {
- // atan2 returns [-180, 180]; normalize to [0, 360]
- orientationAngle += 360;
- }
-
- // Find the nearest orientation.
- // An orientation of 0 can have a nearest angle of 0 or 360 depending
- // on which is closer to the measured orientation angle. We leave the
- // nearest angle at 360 in that case since it makes the delta calculation
- // for orientation angle confidence easier below.
- int nearestOrientation = (orientationAngle + 45) / 90;
- int nearestOrientationAngle = nearestOrientation * 90;
- if (nearestOrientation == 4) {
- nearestOrientation = 0;
- }
-
- // Determine the proposed orientation.
- // The confidence of the proposal is 1.0 when it is ideal and it
- // decays exponentially as the proposal moves further from the ideal
- // angle, tilt and magnitude of the proposed orientation.
- if (isTiltAngleAcceptable(nearestOrientation, tiltAngle)
- && isOrientationAngleAcceptable(nearestOrientation,
- orientationAngle)) {
- proposedOrientation = nearestOrientation;
-
- final float idealOrientationAngle = nearestOrientationAngle;
- final float orientationConfidence = confidence(orientationAngle,
- idealOrientationAngle, ORIENTATION_ANGLE_CONFIDENCE_SCALE);
-
- final float idealTiltAngle = 0;
- final float tiltConfidence = confidence(tiltAngle,
- idealTiltAngle, TILT_ANGLE_CONFIDENCE_SCALE);
-
- final float idealMagnitude = SensorManager.STANDARD_GRAVITY;
- final float magnitudeConfidence = confidence(magnitude,
- idealMagnitude, MAGNITUDE_CONFIDENCE_SCALE);
-
- combinedConfidence = orientationConfidence
- * tiltConfidence * magnitudeConfidence;
-
- if (log) {
- Slog.v(TAG, "Proposal: "
- + "magnitude=" + magnitude
- + ", tiltAngle=" + tiltAngle
- + ", orientationAngle=" + orientationAngle
- + ", proposedOrientation=" + proposedOrientation
- + ", combinedConfidence=" + combinedConfidence
- + ", orientationConfidence=" + orientationConfidence
- + ", tiltConfidence=" + tiltConfidence
- + ", magnitudeConfidence=" + magnitudeConfidence);
- }
- } else {
- if (log) {
- Slog.v(TAG, "Ignoring sensor data, no proposal: "
- + "magnitude=" + magnitude + ", tiltAngle=" + tiltAngle
- + ", orientationAngle=" + orientationAngle);
- }
- }
- }
- }
-
- // Sum up the orientation confidence weights.
- // Detect an orientation change when the sum reaches 1.0.
- final float confidenceAmount = combinedConfidence * timeDeltaMS
- / ORIENTATION_SETTLE_TIME_MS;
- for (int i = 0; i < 4; i++) {
- if (i == proposedOrientation) {
- mConfidence[i] += confidenceAmount;
- if (mConfidence[i] >= 1.0f) {
- mConfidence[i] = 1.0f;
-
- if (i != mRotation) {
- if (log) {
- Slog.v(TAG, "Orientation changed! rotation=" + i);
- }
- mRotation = i;
- orientationChanged = true;
- }
+ final int tiltAngle = (int) Math.round(
+ Math.asin(z / magnitude) * RADIANS_TO_DEGREES);
+
+ // 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) {
+ Slog.v(TAG, "Ignoring sensor data, tilt angle too high: "
+ + "magnitude=" + magnitude + ", tiltAngle=" + tiltAngle);
}
+ clearProposal();
} else {
- mConfidence[i] -= confidenceAmount;
- if (mConfidence[i] < 0.0f) {
- mConfidence[i] = 0.0f;
+ // Calculate the orientation angle.
+ // This is the angle between the x-y projection of the up vector onto
+ // the +y-axis, increasing clockwise in a range of [0, 360] degrees.
+ int orientationAngle = (int) Math.round(
+ -Math.atan2(-x, y) * RADIANS_TO_DEGREES);
+ if (orientationAngle < 0) {
+ // atan2 returns [-180, 180]; normalize to [0, 360]
+ orientationAngle += 360;
+ }
+
+ // Find the nearest rotation.
+ int nearestRotation = (orientationAngle + 45) / 90;
+ if (nearestRotation == 4) {
+ nearestRotation = 0;
+ }
+
+ // Determine the proposed orientation.
+ // The confidence of the proposal is 1.0 when it is ideal and it
+ // decays exponentially as the proposal moves further from the ideal
+ // angle, tilt and magnitude of the proposed orientation.
+ if (!isTiltAngleAcceptable(nearestRotation, tiltAngle)
+ || !isOrientationAngleAcceptable(nearestRotation,
+ orientationAngle)) {
+ if (log) {
+ Slog.v(TAG, "Ignoring sensor data, no proposal: "
+ + "magnitude=" + magnitude + ", tiltAngle=" + tiltAngle
+ + ", orientationAngle=" + orientationAngle);
+ }
+ clearProposal();
+ } else {
+ if (log) {
+ Slog.v(TAG, "Proposal: "
+ + "magnitude=" + magnitude
+ + ", tiltAngle=" + tiltAngle
+ + ", orientationAngle=" + orientationAngle
+ + ", proposalRotation=" + mProposalRotation);
+ }
+ updateProposal(nearestRotation, now / 1000000L,
+ magnitude, tiltAngle, orientationAngle);
}
}
}
}
// Write final statistics about where we are in the orientation detection process.
+ final int proposedRotation = getProposedRotation();
if (log) {
- Slog.v(TAG, "Result: rotation=" + mRotation
- + ", confidence=["
- + mConfidence[0] + ", "
- + mConfidence[1] + ", "
- + mConfidence[2] + ", "
- + mConfidence[3] + "], timeDeltaMS=" + timeDeltaMS);
+ final float proposalConfidence = Math.min(
+ mProposalAgeMS * 1.0f / SETTLE_TIME_MS, 1.0f);
+ Slog.v(TAG, "Result: currentRotation=" + mOrientationListener.mCurrentRotation
+ + ", proposedRotation=" + proposedRotation
+ + ", timeDeltaMS=" + timeDeltaMS
+ + ", proposalRotation=" + mProposalRotation
+ + ", proposalAgeMS=" + mProposalAgeMS
+ + ", proposalConfidence=" + proposalConfidence);
}
// Tell the listener.
- if (orientationChanged) {
- mOrientationListener.onOrientationChanged(mRotation);
+ if (proposedRotation != oldProposedRotation && proposedRotation >= 0) {
+ if (log) {
+ Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + proposedRotation
+ + ", oldProposedRotation=" + oldProposedRotation);
+ }
+ mOrientationListener.onProposedRotationChanged(proposedRotation);
}
}
@@ -541,33 +494,34 @@ public abstract class WindowOrientationListener {
* Returns true if the tilt angle is acceptable for a proposed
* orientation transition.
*/
- private boolean isTiltAngleAcceptable(int proposedOrientation,
+ private boolean isTiltAngleAcceptable(int proposedRotation,
int tiltAngle) {
- return tiltAngle >= TILT_TOLERANCE[proposedOrientation][0]
- && tiltAngle <= TILT_TOLERANCE[proposedOrientation][1];
+ return tiltAngle >= TILT_TOLERANCE[proposedRotation][0]
+ && tiltAngle <= TILT_TOLERANCE[proposedRotation][1];
}
/**
* Returns true if the orientation angle is acceptable for a proposed
* orientation transition.
+ *
* This function takes into account the gap between adjacent orientations
* for hysteresis.
*/
- private boolean isOrientationAngleAcceptable(int proposedOrientation,
- int orientationAngle) {
- final int currentOrientation = mRotation;
-
+ private boolean isOrientationAngleAcceptable(int proposedRotation, int orientationAngle) {
// If there is no current rotation, then there is no gap.
- if (currentOrientation != ROTATION_UNKNOWN) {
- // If the proposed orientation is the same or is counter-clockwise adjacent,
+ // The gap is used only to introduce hysteresis among advertised orientation
+ // changes to avoid flapping.
+ final int currentRotation = mOrientationListener.mCurrentRotation;
+ if (currentRotation >= 0) {
+ // If the proposed rotation is the same or is counter-clockwise adjacent,
// then we set a lower bound on the orientation angle.
- // For example, if currentOrientation is ROTATION_0 and proposed is ROTATION_90,
+ // For example, if currentRotation is ROTATION_0 and proposed is ROTATION_90,
// then we want to check orientationAngle > 45 + GAP / 2.
- if (proposedOrientation == currentOrientation
- || proposedOrientation == (currentOrientation + 1) % 4) {
- int lowerBound = proposedOrientation * 90 - 45
+ if (proposedRotation == currentRotation
+ || proposedRotation == (currentRotation + 1) % 4) {
+ int lowerBound = proposedRotation * 90 - 45
+ ADJACENT_ORIENTATION_ANGLE_GAP / 2;
- if (proposedOrientation == 0) {
+ if (proposedRotation == 0) {
if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) {
return false;
}
@@ -578,15 +532,15 @@ public abstract class WindowOrientationListener {
}
}
- // If the proposed orientation is the same or is clockwise adjacent,
+ // If the proposed rotation is the same or is clockwise adjacent,
// then we set an upper bound on the orientation angle.
- // For example, if currentOrientation is ROTATION_0 and proposed is ROTATION_270,
+ // For example, if currentRotation is ROTATION_0 and proposed is ROTATION_270,
// then we want to check orientationAngle < 315 - GAP / 2.
- if (proposedOrientation == currentOrientation
- || proposedOrientation == (currentOrientation + 3) % 4) {
- int upperBound = proposedOrientation * 90 + 45
+ if (proposedRotation == currentRotation
+ || proposedRotation == (currentRotation + 3) % 4) {
+ int upperBound = proposedRotation * 90 + 45
- ADJACENT_ORIENTATION_ANGLE_GAP / 2;
- if (proposedOrientation == 0) {
+ if (proposedRotation == 0) {
if (orientationAngle <= 45 && orientationAngle > upperBound) {
return false;
}
@@ -600,21 +554,58 @@ public abstract class WindowOrientationListener {
return true;
}
- /**
- * Calculate an exponentially weighted confidence value in the range [0.0, 1.0].
- * The further the value is from the target, the more the confidence trends to 0.
- */
- private static float confidence(float value, float target, float scale) {
- return (float) Math.exp(-Math.abs(value - target) * scale);
+ private void clearProposal() {
+ mProposalRotation = -1;
+ mProposalAgeMS = 0;
}
- /**
- * Calculate a scale factor for the confidence weight exponent.
- * The scale value is chosen such that confidence(value, target, scale) == 0.5
- * whenever abs(value - target) == cutoffDelta.
- */
- private static float confidenceScaleFromDelta(float cutoffDelta) {
- return (float) -Math.log(0.5) / cutoffDelta;
+ private void updateProposal(int rotation, long timestampMS,
+ float magnitude, int tiltAngle, int orientationAngle) {
+ if (mProposalRotation != rotation) {
+ mProposalRotation = rotation;
+ mHistoryIndex = 0;
+ mHistoryLength = 0;
+ }
+
+ final int index = mHistoryIndex;
+ mHistoryTimestampMS[index] = timestampMS;
+ mHistoryMagnitudes[index] = magnitude;
+ mHistoryTiltAngles[index] = tiltAngle;
+ mHistoryOrientationAngles[index] = orientationAngle;
+ mHistoryIndex = (index + 1) % HISTORY_SIZE;
+ if (mHistoryLength < HISTORY_SIZE) {
+ mHistoryLength += 1;
+ }
+
+ long age = 0;
+ for (int i = 1; i < mHistoryLength; i++) {
+ final int olderIndex = (index + HISTORY_SIZE - i) % HISTORY_SIZE;
+ if (Math.abs(mHistoryMagnitudes[olderIndex] - magnitude)
+ > SETTLE_MAGNITUDE_MAX_DELTA) {
+ break;
+ }
+ if (angleAbsoluteDelta(mHistoryTiltAngles[olderIndex],
+ tiltAngle) > SETTLE_TILT_ANGLE_MAX_DELTA) {
+ break;
+ }
+ if (angleAbsoluteDelta(mHistoryOrientationAngles[olderIndex],
+ orientationAngle) > SETTLE_ORIENTATION_ANGLE_MAX_DELTA) {
+ break;
+ }
+ age = timestampMS - mHistoryTimestampMS[olderIndex];
+ if (age >= SETTLE_TIME_MS) {
+ break;
+ }
+ }
+ mProposalAgeMS = age;
+ }
+
+ private static int angleAbsoluteDelta(int a, int b) {
+ int delta = Math.abs(a - b);
+ if (delta > 180) {
+ delta = 360 - delta;
+ }
+ return delta;
}
}
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index a977618..c7d204e 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -446,9 +446,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- public void onOrientationChanged(int rotation) {
- // Send updates based on orientation value
- if (localLOGV) Log.v(TAG, "onOrientationChanged, rotation changed to " +rotation);
+ public void onProposedRotationChanged(int rotation) {
+ if (localLOGV) Log.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
updateRotation(false);
}
}
@@ -654,6 +653,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mKeyguardMediator = new KeyguardViewMediator(context, this, powerManager);
mHandler = new Handler();
mOrientationListener = new MyOrientationListener(mContext);
+ try {
+ mOrientationListener.setCurrentRotation(windowManager.getRotation());
+ } catch (RemoteException ex) { }
SettingsObserver settingsObserver = new SettingsObserver(mHandler);
settingsObserver.observe();
mShortcutManager = new ShortcutManager(context, mHandler);
@@ -2882,7 +2884,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
synchronized (mLock) {
- int sensorRotation = mOrientationListener.getCurrentRotation(); // may be -1
+ int sensorRotation = mOrientationListener.getProposedRotation(); // may be -1
+ if (sensorRotation < 0) {
+ sensorRotation = lastRotation;
+ }
int preferredRotation = -1;
if (mHdmiPlugged) {
@@ -2892,20 +2897,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Ignore sensor when lid switch is open and rotation is forced.
preferredRotation = mLidOpenRotation;
} else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR
- && ((mCarDockEnablesAccelerometer && sensorRotation >= 0)
- || mCarDockRotation >= 0)) {
+ && (mCarDockEnablesAccelerometer || mCarDockRotation >= 0)) {
// Ignore sensor when in car dock unless explicitly enabled.
// This case can override the behavior of NOSENSOR, and can also
// enable 180 degree rotation while docked.
- preferredRotation = mCarDockEnablesAccelerometer && sensorRotation >= 0
+ preferredRotation = mCarDockEnablesAccelerometer
? sensorRotation : mCarDockRotation;
} else if (mDockMode == Intent.EXTRA_DOCK_STATE_DESK
- && ((mDeskDockEnablesAccelerometer && sensorRotation >= 0)
- || mDeskDockRotation >= 0)) {
+ && (mDeskDockEnablesAccelerometer || mDeskDockRotation >= 0)) {
// Ignore sensor when in desk dock unless explicitly enabled.
// This case can override the behavior of NOSENSOR, and can also
// enable 180 degree rotation while docked.
- preferredRotation = mDeskDockEnablesAccelerometer && sensorRotation >= 0
+ preferredRotation = mDeskDockEnablesAccelerometer
? sensorRotation : mDeskDockRotation;
} else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
// Ignore sensor when user locked rotation.
@@ -3006,6 +3009,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
+ @Override
+ public void setRotationLw(int rotation) {
+ mOrientationListener.setCurrentRotation(rotation);
+ }
+
private boolean isLandscapeOrSeascape(int rotation) {
return rotation == mLandscapeRotation || rotation == mSeascapeRotation;
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 02b246a..55784c4 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -5183,6 +5183,7 @@ public class WindowManagerService extends IWindowManager.Stub
mRotation = rotation;
mAltOrientation = altOrientation;
+ mPolicy.setRotationLw(mRotation);
mWindowsFreezingScreen = true;
mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
diff --git a/tools/orientationplot/orientationplot.py b/tools/orientationplot/orientationplot.py
index 07449d4..3a44cb2 100755
--- a/tools/orientationplot/orientationplot.py
+++ b/tools/orientationplot/orientationplot.py
@@ -131,42 +131,28 @@ class Plotter:
self.orientation_angle_axes, 'orientation', 'black')
self._add_timeseries_legend(self.orientation_angle_axes)
- self.actual_orientation = self._make_timeseries()
- self.proposed_orientation = self._make_timeseries()
+ self.current_rotation = self._make_timeseries()
+ self.proposed_rotation = self._make_timeseries()
+ self.proposal_rotation = self._make_timeseries()
self.orientation_axes = self._add_timeseries_axes(
- 5, 'Actual / Proposed Orientation and Confidence', 'rotation', [-1, 4],
+ 5, 'Current / Proposed Orientation and Confidence', 'rotation', [-1, 4],
sharex=shared_axis,
yticks=range(0, 4))
- self.actual_orientation_line = self._add_timeseries_line(
- self.orientation_axes, 'actual', 'black', linewidth=2)
- self.proposed_orientation_line = self._add_timeseries_line(
- self.orientation_axes, 'proposed', 'purple', linewidth=3)
+ self.current_rotation_line = self._add_timeseries_line(
+ self.orientation_axes, 'current', 'black', linewidth=2)
+ self.proposal_rotation_line = self._add_timeseries_line(
+ self.orientation_axes, 'proposal', 'purple', linewidth=3)
+ self.proposed_rotation_line = self._add_timeseries_line(
+ self.orientation_axes, 'proposed', 'green', linewidth=3)
self._add_timeseries_legend(self.orientation_axes)
- self.confidence = [[self._make_timeseries(), self._make_timeseries()] for i in range(0, 4)]
- self.confidence_polys = []
-
- self.combined_confidence = self._make_timeseries()
- self.orientation_confidence = self._make_timeseries()
- self.tilt_confidence = self._make_timeseries()
- self.magnitude_confidence = self._make_timeseries()
- self.confidence_axes = self._add_timeseries_axes(
- 6, 'Proposed Orientation Confidence Factors', 'confidence', [-0.1, 1.1],
- sharex=shared_axis,
- yticks=[0.0, 0.2, 0.4, 0.6, 0.8, 1.0])
- self.combined_confidence_line = self._add_timeseries_line(
- self.confidence_axes, 'combined', 'purple', linewidth=2)
- self.orientation_confidence_line = self._add_timeseries_line(
- self.confidence_axes, 'orientation', 'black')
- self.tilt_confidence_line = self._add_timeseries_line(
- self.confidence_axes, 'tilt', 'brown')
- self.magnitude_confidence_line = self._add_timeseries_line(
- self.confidence_axes, 'magnitude', 'orange')
- self._add_timeseries_legend(self.confidence_axes)
+ self.proposal_confidence = [[self._make_timeseries(), self._make_timeseries()]
+ for i in range(0, 4)]
+ self.proposal_confidence_polys = []
self.sample_latency = self._make_timeseries()
self.sample_latency_axes = self._add_timeseries_axes(
- 7, 'Accelerometer Sampling Latency', 'ms', [-10, 500],
+ 6, 'Accelerometer Sampling Latency', 'ms', [-10, 500],
sharex=shared_axis,
yticks=range(0, 500, 100))
self.sample_latency_line = self._add_timeseries_line(
@@ -186,7 +172,7 @@ class Plotter:
# Add a subplot to the figure for a time series.
def _add_timeseries_axes(self, index, title, ylabel, ylim, yticks, sharex=None):
- num_graphs = 7
+ num_graphs = 6
height = 0.9 / num_graphs
top = 0.95 - height * index
axes = self.fig.add_axes([0.1, top, 0.8, height],
@@ -234,13 +220,10 @@ class Plotter:
self.parse_magnitude = None
self.parse_tilt_angle = None
self.parse_orientation_angle = None
- self.parse_proposed_orientation = None
- self.parse_combined_confidence = None
- self.parse_orientation_confidence = None
- self.parse_tilt_confidence = None
- self.parse_magnitude_confidence = None
- self.parse_actual_orientation = None
- self.parse_confidence = None
+ self.parse_current_rotation = None
+ self.parse_proposed_rotation = None
+ self.parse_proposal_rotation = None
+ self.parse_proposal_confidence = None
self.parse_sample_latency = None
# Update samples.
@@ -284,26 +267,13 @@ class Plotter:
if line.find('orientationAngle=') != -1:
self.parse_orientation_angle = self._get_following_number(line, 'orientationAngle=')
- if line.find('Proposal:') != -1:
- self.parse_proposed_orientation = self._get_following_number(line, 'proposedOrientation=')
- self.parse_combined_confidence = self._get_following_number(line, 'combinedConfidence=')
- self.parse_orientation_confidence = self._get_following_number(line, 'orientationConfidence=')
- self.parse_tilt_confidence = self._get_following_number(line, 'tiltConfidence=')
- self.parse_magnitude_confidence = self._get_following_number(line, 'magnitudeConfidence=')
-
if line.find('Result:') != -1:
- self.parse_actual_orientation = self._get_following_number(line, 'rotation=')
- self.parse_confidence = self._get_following_array_of_numbers(line, 'confidence=')
+ self.parse_current_rotation = self._get_following_number(line, 'currentRotation=')
+ self.parse_proposed_rotation = self._get_following_number(line, 'proposedRotation=')
+ self.parse_proposal_rotation = self._get_following_number(line, 'proposalRotation=')
+ self.parse_proposal_confidence = self._get_following_number(line, 'proposalConfidence=')
self.parse_sample_latency = self._get_following_number(line, 'timeDeltaMS=')
- for i in range(0, 4):
- if self.parse_confidence is not None:
- self._append(self.confidence[i][0], timeindex, i)
- self._append(self.confidence[i][1], timeindex, i + self.parse_confidence[i])
- else:
- self._append(self.confidence[i][0], timeindex, None)
- self._append(self.confidence[i][1], timeindex, None)
-
self._append(self.raw_acceleration_x, timeindex, self.parse_raw_acceleration_x)
self._append(self.raw_acceleration_y, timeindex, self.parse_raw_acceleration_y)
self._append(self.raw_acceleration_z, timeindex, self.parse_raw_acceleration_z)
@@ -313,12 +283,22 @@ class Plotter:
self._append(self.magnitude, timeindex, self.parse_magnitude)
self._append(self.tilt_angle, timeindex, self.parse_tilt_angle)
self._append(self.orientation_angle, timeindex, self.parse_orientation_angle)
- self._append(self.actual_orientation, timeindex, self.parse_actual_orientation)
- self._append(self.proposed_orientation, timeindex, self.parse_proposed_orientation)
- self._append(self.combined_confidence, timeindex, self.parse_combined_confidence)
- self._append(self.orientation_confidence, timeindex, self.parse_orientation_confidence)
- self._append(self.tilt_confidence, timeindex, self.parse_tilt_confidence)
- self._append(self.magnitude_confidence, timeindex, self.parse_magnitude_confidence)
+ self._append(self.current_rotation, timeindex, self.parse_current_rotation)
+ if self.parse_proposed_rotation >= 0:
+ self._append(self.proposed_rotation, timeindex, self.parse_proposed_rotation)
+ else:
+ self._append(self.proposed_rotation, timeindex, None)
+ if self.parse_proposal_rotation >= 0:
+ self._append(self.proposal_rotation, timeindex, self.parse_proposal_rotation)
+ else:
+ self._append(self.proposal_rotation, timeindex, None)
+ for i in range(0, 4):
+ self._append(self.proposal_confidence[i][0], timeindex, i)
+ if i == self.parse_proposal_rotation:
+ self._append(self.proposal_confidence[i][1], timeindex,
+ i + self.parse_proposal_confidence)
+ else:
+ self._append(self.proposal_confidence[i][1], timeindex, i)
self._append(self.sample_latency, timeindex, self.parse_sample_latency)
self._reset_parse_state()
@@ -335,16 +315,13 @@ class Plotter:
self._scroll(self.magnitude, bottom)
self._scroll(self.tilt_angle, bottom)
self._scroll(self.orientation_angle, bottom)
- self._scroll(self.actual_orientation, bottom)
- self._scroll(self.proposed_orientation, bottom)
- self._scroll(self.combined_confidence, bottom)
- self._scroll(self.orientation_confidence, bottom)
- self._scroll(self.tilt_confidence, bottom)
- self._scroll(self.magnitude_confidence, bottom)
- self._scroll(self.sample_latency, bottom)
+ self._scroll(self.current_rotation, bottom)
+ self._scroll(self.proposed_rotation, bottom)
+ self._scroll(self.proposal_rotation, bottom)
for i in range(0, 4):
- self._scroll(self.confidence[i][0], bottom)
- self._scroll(self.confidence[i][1], bottom)
+ self._scroll(self.proposal_confidence[i][0], bottom)
+ self._scroll(self.proposal_confidence[i][1], bottom)
+ self._scroll(self.sample_latency, bottom)
# Redraw the plots.
self.raw_acceleration_line_x.set_data(self.raw_acceleration_x)
@@ -356,20 +333,19 @@ class Plotter:
self.magnitude_line.set_data(self.magnitude)
self.tilt_angle_line.set_data(self.tilt_angle)
self.orientation_angle_line.set_data(self.orientation_angle)
- self.actual_orientation_line.set_data(self.actual_orientation)
- self.proposed_orientation_line.set_data(self.proposed_orientation)
- self.combined_confidence_line.set_data(self.combined_confidence)
- self.orientation_confidence_line.set_data(self.orientation_confidence)
- self.tilt_confidence_line.set_data(self.tilt_confidence)
- self.magnitude_confidence_line.set_data(self.magnitude_confidence)
+ self.current_rotation_line.set_data(self.current_rotation)
+ self.proposed_rotation_line.set_data(self.proposed_rotation)
+ self.proposal_rotation_line.set_data(self.proposal_rotation)
self.sample_latency_line.set_data(self.sample_latency)
- for poly in self.confidence_polys:
+ for poly in self.proposal_confidence_polys:
poly.remove()
- self.confidence_polys = []
+ self.proposal_confidence_polys = []
for i in range(0, 4):
- self.confidence_polys.append(self.orientation_axes.fill_between(self.confidence[i][0][0],
- self.confidence[i][0][1], self.confidence[i][1][1],
+ self.proposal_confidence_polys.append(self.orientation_axes.fill_between(
+ self.proposal_confidence[i][0][0],
+ self.proposal_confidence[i][0][1],
+ self.proposal_confidence[i][1][1],
facecolor='goldenrod', edgecolor='goldenrod'))
self.fig.canvas.draw_idle()