diff options
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() |