diff options
author | Jeff Brown <jeffbrown@google.com> | 2011-09-23 17:26:09 -0700 |
---|---|---|
committer | Jeff Brown <jeffbrown@google.com> | 2011-09-23 17:26:09 -0700 |
commit | c0347aa19f354a8e1ff4fcd5372b134c0c7c16ad (patch) | |
tree | b442f6284e23f5b203538adb123e9fe2b704fd43 | |
parent | 745f1e3a06eb504c9e4465afc987854a6269220d (diff) | |
download | frameworks_base-c0347aa19f354a8e1ff4fcd5372b134c0c7c16ad.zip frameworks_base-c0347aa19f354a8e1ff4fcd5372b134c0c7c16ad.tar.gz frameworks_base-c0347aa19f354a8e1ff4fcd5372b134c0c7c16ad.tar.bz2 |
Prevent unintended rotations.
Bug: 4981385
Changed the orientation listener to notify the policy whenever
its proposed orientation changes, and changes the window manager
to notify the orientation listener when the actual orientation
changes. This allows us to better handle the case where the
policy has rejected a given proposal at one time (because the
current application forced orientation) but might choose
to accept the same proposal at another time.
It's important that the proposal always be up to date. A proposal
becomes irrelevant as soon as the phone posture changes such
that we can no longer determine the orientation with confidence
(such as when a device is placed flat on a table).
Simplified the orientation filtering. Now we just wait 200ms
for the device to be still before issuing a proposal. The idea
is that if the device is moving around a lot, we assume that
the device is being picked up or put down or otherwise in
the process of being moved. We don't want to change the rotation
until that's all settled down. However, we do want to tolerate
a certain amount of environmental noise.
(The previous confidence algorithm was also designed along
these lines but it was less direct about waiting for things
to settle. Instead it simply made orientation changes take
longer than usual while unsettled, but the extra delay was often
too much or too little. This one should be easier to tune.)
Change-Id: I09e6befea1f0994b6b15d424f3182859c0d9a530
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() |