19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17eee29c445c38217e83bf421faf0f4075322079a6Craig Mautnerpackage com.android.internal.policy.impl;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.hardware.Sensor;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.hardware.SensorEvent;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.hardware.SensorEventListener;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.hardware.SensorManager;
24eee29c445c38217e83bf421faf0f4075322079a6Craig Mautnerimport android.os.Handler;
25daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brownimport android.os.SystemProperties;
265aa73ae58f049379a97bc86add541f27170c02a4Jeff Brownimport android.util.FloatMath;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
284519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brownimport android.util.Slog;
29600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brownimport android.util.TimeUtils;
30600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown
31600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brownimport java.io.PrintWriter;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * A special helper class used by the WindowManager
35c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * for receiving notifications from the SensorManager when
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the orientation of the device has changed.
37e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn *
38e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn * NOTE: If changing anything here, please run the API demo
39e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn * "App/Activity/Screen Orientation" to ensure that all orientation
40e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn * modes still work correctly.
41e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn *
42daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown * You can also visualize the behavior of the WindowOrientationListener.
43daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown * Refer to frameworks/base/tools/orientationplot/README.txt for details.
444519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown *
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @hide
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic abstract class WindowOrientationListener {
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String TAG = "WindowOrientationListener";
49daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown    private static final boolean LOG = SystemProperties.getBoolean(
50daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown            "debug.orientation.log", false);
514519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown
525aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown    private static final boolean USE_GRAVITY_SENSOR = false;
535aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
54eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner    private Handler mHandler;
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private SensorManager mSensorManager;
564519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown    private boolean mEnabled;
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mRate;
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Sensor mSensor;
5963104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu    private SensorEventListenerImpl mSensorEventListener;
60eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner    private int mCurrentRotation = -1;
61eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner
62eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner    private final Object mLock = new Object();
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Creates a new WindowOrientationListener.
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param context for the WindowOrientationListener.
68eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner     * @param handler Provides the Looper for receiving sensor updates.
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
70eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner    public WindowOrientationListener(Context context, Handler handler) {
71eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        this(context, handler, SensorManager.SENSOR_DELAY_UI);
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Creates a new WindowOrientationListener.
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param context for the WindowOrientationListener.
78eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner     * @param handler Provides the Looper for receiving sensor updates.
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param rate at which sensor events are processed (see also
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link android.hardware.SensorManager SensorManager}). Use the default
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * SENSOR_DELAY_NORMAL} for simple screen orientation change detection.
831ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard     *
844519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     * This constructor is private since no one uses it.
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
86eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner    private WindowOrientationListener(Context context, Handler handler, int rate) {
87eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        mHandler = handler;
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mRate = rate;
905aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR
915aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER);
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mSensor != null) {
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Create listener only if sensors do exist
94eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            mSensorEventListener = new SensorEventListenerImpl();
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Enables the WindowOrientationListener so it will monitor the sensor and call
100eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner     * {@link #onProposedRotationChanged(int)} when the device orientation changes.
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void enable() {
103eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        synchronized (mLock) {
104eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            if (mSensor == null) {
105eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                Log.w(TAG, "Cannot detect sensors. Not enabled");
106eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                return;
107eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            }
108eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            if (mEnabled == false) {
109eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                if (LOG) {
110eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                    Log.d(TAG, "WindowOrientationListener enabled");
111eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                }
112eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                mSensorEventListener.resetLocked();
113eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                mSensorManager.registerListener(mSensorEventListener, mSensor, mRate, mHandler);
114eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                mEnabled = true;
115daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown            }
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Disables the WindowOrientationListener.
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void disable() {
123eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        synchronized (mLock) {
124eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            if (mSensor == null) {
125eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                Log.w(TAG, "Cannot detect sensors. Invalid disable");
126eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                return;
127eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            }
128eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            if (mEnabled == true) {
129eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                if (LOG) {
130eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                    Log.d(TAG, "WindowOrientationListener disabled");
131eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                }
132eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                mSensorManager.unregisterListener(mSensorEventListener);
133eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                mEnabled = false;
134daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown            }
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1384519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown    /**
139c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown     * Sets the current rotation.
140c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown     *
141c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown     * @param rotation The current rotation.
142c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown     */
143c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown    public void setCurrentRotation(int rotation) {
144eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        synchronized (mLock) {
145eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            mCurrentRotation = rotation;
146eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        }
147c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown    }
148c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown
149c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown    /**
150c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown     * Gets the proposed rotation.
151c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown     *
152c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown     * This method only returns a rotation if the orientation listener is certain
153c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown     * of its proposal.  If the rotation is indeterminate, returns -1.
154c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown     *
155c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown     * @return The proposed rotation, or -1 if unknown.
1564519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     */
157c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown    public int getProposedRotation() {
158eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        synchronized (mLock) {
159eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            if (mEnabled) {
160eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                return mSensorEventListener.getProposedRotationLocked();
161eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            }
162eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            return -1;
16363104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu        }
164e4fbd6235c8d1c5b0ed4883ec275dd3fc9c919fbDianne Hackborn    }
1655f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard
1665f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard    /**
1674519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     * Returns true if sensor is enabled and false otherwise
1684519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     */
1694519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown    public boolean canDetectOrientation() {
170eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        synchronized (mLock) {
171eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            return mSensor != null;
172eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        }
1734519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown    }
1744519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown
1754519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown    /**
1764519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     * Called when the rotation view of the device has changed.
1774519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *
178c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown     * This method is called whenever the orientation becomes certain of an orientation.
179c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown     * It is called each time the orientation determination transitions from being
180c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown     * uncertain to being certain again, even if it is the same orientation as before.
181c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown     *
1824519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     * @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants.
183eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner     * @see android.view.Surface
1844519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     */
185c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown    public abstract void onProposedRotationChanged(int rotation);
1864519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown
187600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown    public void dump(PrintWriter pw, String prefix) {
188600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown        synchronized (mLock) {
189600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            pw.println(prefix + TAG);
190600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            prefix += "  ";
191600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            pw.println(prefix + "mEnabled=" + mEnabled);
192600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            pw.println(prefix + "mCurrentRotation=" + mCurrentRotation);
193600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            pw.println(prefix + "mSensor=" + mSensor);
194600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            pw.println(prefix + "mRate=" + mRate);
195600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown
1960abc28e1141aa5afb1a4e3dc65a8c658dc7ebbf8Jaewan Kim            if (mSensorEventListener != null) {
1970abc28e1141aa5afb1a4e3dc65a8c658dc7ebbf8Jaewan Kim                mSensorEventListener.dumpLocked(pw, prefix);
1980abc28e1141aa5afb1a4e3dc65a8c658dc7ebbf8Jaewan Kim            }
199600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown        }
200600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown    }
201600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown
2024519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown    /**
2035f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard     * This class filters the raw accelerometer data and tries to detect actual changes in
2045f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard     * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters,
2055f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard     * but here's the outline:
2065f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard     *
2074519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *  - Low-pass filter the accelerometer vector in cartesian coordinates.  We do it in
2084519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *    cartesian space because the orientation calculations are sensitive to the
2094519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *    absolute magnitude of the acceleration.  In particular, there are singularities
2104519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *    in the calculation as the magnitude approaches 0.  By performing the low-pass
2115aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown     *    filtering early, we can eliminate most spurious high-frequency impulses due to noise.
2124519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *
2134519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *  - Convert the acceleromter vector from cartesian to spherical coordinates.
2144519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *    Since we're dealing with rotation of the device, this is the sensible coordinate
2154519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *    system to work in.  The zenith direction is the Z-axis, the direction the screen
2164519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *    is facing.  The radial distance is referred to as the magnitude below.
2174519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *    The elevation angle is referred to as the "tilt" below.
2184519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *    The azimuth angle is referred to as the "orientation" below (and the azimuth axis is
2194519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *    the Y-axis).
2204519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *    See http://en.wikipedia.org/wiki/Spherical_coordinate_system for reference.
2214519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *
2224519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *  - If the tilt angle is too close to horizontal (near 90 or -90 degrees), do nothing.
2234519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *    The orientation angle is not meaningful when the device is nearly horizontal.
2244519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *    The tilt angle thresholds are set differently for each orientation and different
2254519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *    limits are applied when the device is facing down as opposed to when it is facing
2264519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *    forward or facing up.
2275f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard     *
2284519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *  - When the orientation angle reaches a certain threshold, consider transitioning
2294519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *    to the corresponding orientation.  These thresholds have some hysteresis built-in
2304519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *    to avoid oscillations between adjacent orientations.
2315f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard     *
232c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown     *  - Wait for the device to settle for a little bit.  Once that happens, issue the
233c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown     *    new orientation proposal.
2344519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     *
2354519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown     * Details are explained inline.
2365aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown     *
2375aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown     * See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for
2385aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown     * signal processing background.
2395f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard     */
240eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner    final class SensorEventListenerImpl implements SensorEventListener {
2411ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard        // We work with all angles in degrees in this class.
2421ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard        private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI);
2431ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard
2445aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // Number of nanoseconds per millisecond.
2455aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private static final long NANOS_PER_MS = 1000000;
2465aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
2474519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // Indices into SensorEvent.values for the accelerometer sensor.
2484519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        private static final int ACCELEROMETER_DATA_X = 0;
2494519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        private static final int ACCELEROMETER_DATA_Y = 1;
2504519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        private static final int ACCELEROMETER_DATA_Z = 2;
2514519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown
2525aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // The minimum amount of time that a predicted rotation must be stable before it
2535aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // is accepted as a valid rotation proposal.  This value can be quite small because
2545aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // the low-pass filter already suppresses most of the noise so we're really just
2555aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // looking for quick confirmation that the last few samples are in agreement as to
2565aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // the desired orientation.
2575aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private static final long PROPOSAL_SETTLE_TIME_NANOS = 40 * NANOS_PER_MS;
2585aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
2595aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // The minimum amount of time that must have elapsed since the device last exited
2605aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // the flat state (time since it was picked up) before the proposed rotation
2615aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // can change.
2625aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private static final long PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS = 500 * NANOS_PER_MS;
2635aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
264daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown        // The minimum amount of time that must have elapsed since the device stopped
2655aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // swinging (time since device appeared to be in the process of being put down
2665aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // or put away into a pocket) before the proposed rotation can change.
2675aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private static final long PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS = 300 * NANOS_PER_MS;
2685aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
269daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown        // The minimum amount of time that must have elapsed since the device stopped
270daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown        // undergoing external acceleration before the proposed rotation can change.
271daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown        private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS =
272daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown                500 * NANOS_PER_MS;
273daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown
2745aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // If the tilt angle remains greater than the specified angle for a minimum of
2755aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // the specified time, then the device is deemed to be lying flat
2765aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // (just chillin' on a table).
2775aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private static final float FLAT_ANGLE = 75;
2785aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private static final long FLAT_TIME_NANOS = 1000 * NANOS_PER_MS;
2795aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
2805aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // If the tilt angle has increased by at least delta degrees within the specified amount
2815aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // of time, then the device is deemed to be swinging away from the user
2825aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // down towards flat (tilt = 90).
2835aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private static final float SWING_AWAY_ANGLE_DELTA = 20;
2845aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private static final long SWING_TIME_NANOS = 300 * NANOS_PER_MS;
285c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown
2864519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // The maximum sample inter-arrival time in milliseconds.
2874519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // If the acceleration samples are further apart than this amount in time, we reset the
2884519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // state of the low-pass filter and orientation properties.  This helps to handle
2894519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // boundary conditions when the device is turned on, wakes from suspend or there is
2904519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // a significant gap in samples.
2915aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private static final long MAX_FILTER_DELTA_TIME_NANOS = 1000 * NANOS_PER_MS;
2924519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown
293c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        // The acceleration filter time constant.
294c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        //
295c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        // This time constant is used to tune the acceleration filter such that
296c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        // impulses and vibrational noise (think car dock) is suppressed before we
297c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        // try to calculate the tilt and orientation angles.
298c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        //
299c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        // The filter time constant is related to the filter cutoff frequency, which is the
300c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        // frequency at which signals are attenuated by 3dB (half the passband power).
3014519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // Each successive octave beyond this frequency is attenuated by an additional 6dB.
3024519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        //
303c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        // Given a time constant t in seconds, the filter cutoff frequency Fc in Hertz
304c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        // is given by Fc = 1 / (2pi * t).
305c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        //
306c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        // The higher the time constant, the lower the cutoff frequency, so more noise
307c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        // will be suppressed.
308c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        //
309c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        // Filtering adds latency proportional the time constant (inversely proportional
310c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        // to the cutoff frequency) so we don't want to make the time constant too
3115aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // large or we can lose responsiveness.  Likewise we don't want to make it too
3125aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // small or we do a poor job suppressing acceleration spikes.
3135aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // Empirically, 100ms seems to be too small and 500ms is too large.
3145aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private static final float FILTER_TIME_CONSTANT_MS = 200.0f;
3154519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown
3164519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        /* State for orientation detection. */
3174519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown
3184519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // Thresholds for minimum and maximum allowable deviation from gravity.
3194519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        //
3204519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // If the device is undergoing external acceleration (being bumped, in a car
3214519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // that is turning around a corner or a plane taking off) then the magnitude
3224519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // may be substantially more or less than gravity.  This can skew our orientation
3234519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // detection by making us think that up is pointed in a different direction.
3244519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        //
3254519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // Conversely, if the device is in freefall, then there will be no gravity to
3264519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // measure at all.  This is problematic because we cannot detect the orientation
3274519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // without gravity to tell us which way is up.  A magnitude near 0 produces
3284519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // singularities in the tilt and orientation calculations.
3294519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        //
3304519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // In both cases, we postpone choosing an orientation.
331daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown        //
332daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown        // However, we need to tolerate some acceleration because the angular momentum
333daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown        // of turning the device can skew the observed acceleration for a short period of time.
334daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown        private static final float NEAR_ZERO_MAGNITUDE = 1; // m/s^2
335daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown        private static final float ACCELERATION_TOLERANCE = 4; // m/s^2
3364519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        private static final float MIN_ACCELERATION_MAGNITUDE =
337daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown                SensorManager.STANDARD_GRAVITY - ACCELERATION_TOLERANCE;
3384519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        private static final float MAX_ACCELERATION_MAGNITUDE =
339daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown            SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE;
34063104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu
3415f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard        // Maximum absolute tilt angle at which to consider orientation data.  Beyond this (i.e.
3425f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard        // when screen is facing the sky or ground), we completely ignore orientation data.
3435f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard        private static final int MAX_TILT = 75;
3441ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard
3454519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // The tilt angle range in degrees for each orientation.
3464519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // Beyond these tilt angles, we don't even consider transitioning into the
3474519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // specified orientation.  We place more stringent requirements on unnatural
3484519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // orientations than natural ones to make it less likely to accidentally transition
3494519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // into those states.
3504519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // The first value of each pair is negative so it applies a limit when the device is
3514519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // facing down (overhead reading in bed).
3524519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // The second value of each pair is positive so it applies a limit when the device is
3534519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // facing up (resting on a table).
3544519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // The ideal tilt angle is 0 (when the device is vertical) so the limits establish
3554519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // how close to vertical the device must be in order to change orientation.
356eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        private final int[][] TILT_TOLERANCE = new int[][] {
3575aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            /* ROTATION_0   */ { -25, 70 },
3585aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            /* ROTATION_90  */ { -25, 65 },
3595aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            /* ROTATION_180 */ { -25, 60 },
3605aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            /* ROTATION_270 */ { -25, 65 }
3614519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        };
3625f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard
363600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown        // The tilt angle below which we conclude that the user is holding the device
364600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown        // overhead reading in bed and lock into that state.
365600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown        private final int TILT_OVERHEAD_ENTER = -40;
366600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown
367600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown        // The tilt angle above which we conclude that the user would like a rotation
368600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown        // change to occur and unlock from the overhead state.
369600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown        private final int TILT_OVERHEAD_EXIT = -15;
370600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown
3714519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // The gap angle in degrees between adjacent orientation angles for hysteresis.
3724519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // This creates a "dead zone" between the current orientation and a proposed
3734519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // adjacent orientation.  No orientation proposal is made when the orientation
3744519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // angle is within the gap between the current orientation and the adjacent
3754519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        // orientation.
376c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 45;
37763104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu
3785aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // Timestamp and value of the last accelerometer sample.
3795aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private long mLastFilteredTimestampNanos;
3805aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private float mLastFilteredX, mLastFilteredY, mLastFilteredZ;
3815aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
3825aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // The last proposed rotation, -1 if unknown.
3835aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private int mProposedRotation;
3845aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
3855aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // Value of the current predicted rotation, -1 if unknown.
3865aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private int mPredictedRotation;
3875aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
3885aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // Timestamp of when the predicted rotation most recently changed.
3895aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private long mPredictedRotationTimestampNanos;
390e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn
3915aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // Timestamp when the device last appeared to be flat for sure (the flat delay elapsed).
3925aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private long mFlatTimestampNanos;
393600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown        private boolean mFlat;
3941ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard
3955aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // Timestamp when the device last appeared to be swinging.
3965aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private long mSwingTimestampNanos;
397600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown        private boolean mSwinging;
3981ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard
399daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown        // Timestamp when the device last appeared to be undergoing external acceleration.
400daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown        private long mAccelerationTimestampNanos;
401600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown        private boolean mAccelerating;
402600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown
403600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown        // Whether we are locked into an overhead usage mode.
404600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown        private boolean mOverhead;
405daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown
4065aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        // History of observed tilt angles.
4075aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private static final int TILT_HISTORY_SIZE = 40;
4085aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private float[] mTiltHistory = new float[TILT_HISTORY_SIZE];
4095aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private long[] mTiltHistoryTimestampNanos = new long[TILT_HISTORY_SIZE];
4105aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        private int mTiltHistoryIndex;
4111ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard
412eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        public int getProposedRotationLocked() {
4135aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            return mProposedRotation;
4141ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard        }
4151ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard
416600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown        public void dumpLocked(PrintWriter pw, String prefix) {
417600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            pw.println(prefix + "mProposedRotation=" + mProposedRotation);
418600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            pw.println(prefix + "mPredictedRotation=" + mPredictedRotation);
419600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            pw.println(prefix + "mLastFilteredX=" + mLastFilteredX);
420600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            pw.println(prefix + "mLastFilteredY=" + mLastFilteredY);
421600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            pw.println(prefix + "mLastFilteredZ=" + mLastFilteredZ);
422600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            pw.println(prefix + "mTiltHistory={last: " + getLastTiltLocked() + "}");
423600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            pw.println(prefix + "mFlat=" + mFlat);
424600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            pw.println(prefix + "mSwinging=" + mSwinging);
425600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            pw.println(prefix + "mAccelerating=" + mAccelerating);
426600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            pw.println(prefix + "mOverhead=" + mOverhead);
427600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown        }
428600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown
4294519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        @Override
4304519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        public void onAccuracyChanged(Sensor sensor, int accuracy) {
43163104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu        }
43263104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu
4334519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown        @Override
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void onSensorChanged(SensorEvent event) {
435eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            int proposedRotation;
436eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            int oldProposedRotation;
437eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner
438eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            synchronized (mLock) {
439eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                // The vector given in the SensorEvent points straight up (towards the sky) under
440eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                // ideal conditions (the phone is not accelerating).  I'll call this up vector
441eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                // elsewhere.
442eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                float x = event.values[ACCELEROMETER_DATA_X];
443eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                float y = event.values[ACCELEROMETER_DATA_Y];
444eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                float z = event.values[ACCELEROMETER_DATA_Z];
4455f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard
446daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown                if (LOG) {
447eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                    Slog.v(TAG, "Raw acceleration vector: "
4485aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                            + "x=" + x + ", y=" + y + ", z=" + z
4495aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                            + ", magnitude=" + FloatMath.sqrt(x * x + y * y + z * z));
4504519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                }
451eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner
452eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                // Apply a low-pass filter to the acceleration up vector in cartesian space.
453eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                // Reset the orientation listener state if the samples are too far apart in time
454eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                // or when we see values of (0, 0, 0) which indicates that we polled the
455eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                // accelerometer too soon after turning it on and we don't have any data yet.
456eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                final long now = event.timestamp;
457eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                final long then = mLastFilteredTimestampNanos;
458eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                final float timeDeltaMS = (now - then) * 0.000001f;
459eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                final boolean skipSample;
460eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                if (now < then
461eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        || now > then + MAX_FILTER_DELTA_TIME_NANOS
462eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        || (x == 0 && y == 0 && z == 0)) {
463daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown                    if (LOG) {
464eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        Slog.v(TAG, "Resetting orientation listener.");
4654519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                    }
466eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                    resetLocked();
467eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                    skipSample = true;
4684519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                } else {
469eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                    final float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS);
470eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                    x = alpha * (x - mLastFilteredX) + mLastFilteredX;
471eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                    y = alpha * (y - mLastFilteredY) + mLastFilteredY;
472eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                    z = alpha * (z - mLastFilteredZ) + mLastFilteredZ;
473eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                    if (LOG) {
474eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        Slog.v(TAG, "Filtered acceleration vector: "
475eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                + "x=" + x + ", y=" + y + ", z=" + z
476eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                + ", magnitude=" + FloatMath.sqrt(x * x + y * y + z * z));
4775aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                    }
478eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                    skipSample = false;
479eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                }
480eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                mLastFilteredTimestampNanos = now;
481eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                mLastFilteredX = x;
482eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                mLastFilteredY = y;
483eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                mLastFilteredZ = z;
484eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner
485eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                boolean isAccelerating = false;
486eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                boolean isFlat = false;
487eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                boolean isSwinging = false;
488eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                if (!skipSample) {
489eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                    // Calculate the magnitude of the acceleration vector.
490eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                    final float magnitude = FloatMath.sqrt(x * x + y * y + z * z);
491eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                    if (magnitude < NEAR_ZERO_MAGNITUDE) {
492daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown                        if (LOG) {
493eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            Slog.v(TAG, "Ignoring sensor data, magnitude too close to zero.");
4944519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                        }
495eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        clearPredictedRotationLocked();
4964519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                    } else {
497eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        // Determine whether the device appears to be undergoing external
498eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        // acceleration.
499eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        if (isAcceleratingLocked(magnitude)) {
500eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            isAccelerating = true;
501eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            mAccelerationTimestampNanos = now;
502c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown                        }
503c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown
504eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        // Calculate the tilt angle.
505eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        // This is the angle between the up vector and the x-y plane (the plane of
506eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        // the screen) in a range of [-90, 90] degrees.
507eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        //   -90 degrees: screen horizontal and facing the ground (overhead)
508eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        //     0 degrees: screen vertical
509eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        //    90 degrees: screen horizontal and facing the sky (on table)
510eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        final int tiltAngle = (int) Math.round(
511eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                Math.asin(z / magnitude) * RADIANS_TO_DEGREES);
512eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        addTiltHistoryEntryLocked(now, tiltAngle);
513eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner
514eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        // Determine whether the device appears to be flat or swinging.
515eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        if (isFlatLocked(now)) {
516eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            isFlat = true;
517eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            mFlatTimestampNanos = now;
518eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        }
519eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        if (isSwingingLocked(now, tiltAngle)) {
520eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            isSwinging = true;
521eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            mSwingTimestampNanos = now;
522c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown                        }
523c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown
524eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        // If the tilt angle is too close to horizontal then we cannot determine
525eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                        // the orientation angle of the screen.
526600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown                        if (tiltAngle <= TILT_OVERHEAD_ENTER) {
527600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown                            mOverhead = true;
528600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown                        } else if (tiltAngle >= TILT_OVERHEAD_EXIT) {
529600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown                            mOverhead = false;
530600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown                        }
531600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown                        if (mOverhead) {
532600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown                            if (LOG) {
533600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown                                Slog.v(TAG, "Ignoring sensor data, device is overhead: "
534600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown                                        + "tiltAngle=" + tiltAngle);
535600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown                            }
536600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown                            clearPredictedRotationLocked();
537600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown                        } else if (Math.abs(tiltAngle) > MAX_TILT) {
538daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown                            if (LOG) {
539eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                Slog.v(TAG, "Ignoring sensor data, tilt angle too high: "
540eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                        + "tiltAngle=" + tiltAngle);
541c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown                            }
542eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            clearPredictedRotationLocked();
543c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown                        } else {
544eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            // Calculate the orientation angle.
545eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            // This is the angle between the x-y projection of the up vector onto
546eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            // the +y-axis, increasing clockwise in a range of [0, 360] degrees.
547eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            int orientationAngle = (int) Math.round(
548eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                    -Math.atan2(-x, y) * RADIANS_TO_DEGREES);
549eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            if (orientationAngle < 0) {
550eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                // atan2 returns [-180, 180]; normalize to [0, 360]
551eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                orientationAngle += 360;
552eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            }
553eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner
554eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            // Find the nearest rotation.
555eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            int nearestRotation = (orientationAngle + 45) / 90;
556eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            if (nearestRotation == 4) {
557eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                nearestRotation = 0;
558eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            }
559eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner
560eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            // Determine the predicted orientation.
561eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            if (isTiltAngleAcceptableLocked(nearestRotation, tiltAngle)
562eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                    && isOrientationAngleAcceptableLocked(nearestRotation,
563eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                            orientationAngle)) {
564eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                updatePredictedRotationLocked(now, nearestRotation);
565eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                if (LOG) {
566eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                    Slog.v(TAG, "Predicted: "
567eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                            + "tiltAngle=" + tiltAngle
568eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                            + ", orientationAngle=" + orientationAngle
569eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                            + ", predictedRotation=" + mPredictedRotation
570eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                            + ", predictedRotationAgeMS="
571eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                                    + ((now - mPredictedRotationTimestampNanos)
572eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                                            * 0.000001f));
573eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                }
574eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            } else {
575eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                if (LOG) {
576eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                    Slog.v(TAG, "Ignoring sensor data, no predicted rotation: "
577eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                            + "tiltAngle=" + tiltAngle
578eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                            + ", orientationAngle=" + orientationAngle);
579eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                }
580eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                clearPredictedRotationLocked();
581c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown                            }
5824519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                        }
5834519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                    }
5844519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                }
585600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown                mFlat = isFlat;
586600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown                mSwinging = isSwinging;
587600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown                mAccelerating = isAccelerating;
5885f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard
589eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                // Determine new proposed rotation.
590eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                oldProposedRotation = mProposedRotation;
591eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                if (mPredictedRotation < 0 || isPredictedRotationAcceptableLocked(now)) {
592eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                    mProposedRotation = mPredictedRotation;
593eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                }
594eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                proposedRotation = mProposedRotation;
5955aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
596eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                // Write final statistics about where we are in the orientation detection process.
597eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                if (LOG) {
598eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                    Slog.v(TAG, "Result: currentRotation=" + mCurrentRotation
599eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            + ", proposedRotation=" + proposedRotation
600eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            + ", predictedRotation=" + mPredictedRotation
601eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            + ", timeDeltaMS=" + timeDeltaMS
602eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            + ", isAccelerating=" + isAccelerating
603eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            + ", isFlat=" + isFlat
604eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            + ", isSwinging=" + isSwinging
605600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown                            + ", isOverhead=" + mOverhead
606eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            + ", timeUntilSettledMS=" + remainingMS(now,
607eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                    mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS)
608eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            + ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now,
609eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                    mAccelerationTimestampNanos + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS)
610eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            + ", timeUntilFlatDelayExpiredMS=" + remainingMS(now,
611eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                    mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS)
612eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                            + ", timeUntilSwingDelayExpiredMS=" + remainingMS(now,
613eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                                    mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS));
614eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                }
6154519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown            }
6164519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown
6174519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown            // Tell the listener.
618eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            if (proposedRotation != oldProposedRotation && proposedRotation >= 0) {
619daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown                if (LOG) {
620eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                    Slog.v(TAG, "Proposed rotation changed!  proposedRotation=" + proposedRotation
621c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown                            + ", oldProposedRotation=" + oldProposedRotation);
622c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown                }
623eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner                onProposedRotationChanged(proposedRotation);
6244519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown            }
6255f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard        }
6265f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard
6275f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard        /**
6285aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown         * Returns true if the tilt angle is acceptable for a given predicted rotation.
6295f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard         */
630eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        private boolean isTiltAngleAcceptableLocked(int rotation, int tiltAngle) {
6315aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            return tiltAngle >= TILT_TOLERANCE[rotation][0]
6325aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                    && tiltAngle <= TILT_TOLERANCE[rotation][1];
6335f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard        }
6345f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard
6355f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard        /**
6365aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown         * Returns true if the orientation angle is acceptable for a given predicted rotation.
637c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown         *
6384519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown         * This function takes into account the gap between adjacent orientations
6394519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown         * for hysteresis.
6405f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard         */
641eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        private boolean isOrientationAngleAcceptableLocked(int rotation, int orientationAngle) {
6424519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown            // If there is no current rotation, then there is no gap.
643c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown            // The gap is used only to introduce hysteresis among advertised orientation
644c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown            // changes to avoid flapping.
645eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            final int currentRotation = mCurrentRotation;
646c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown            if (currentRotation >= 0) {
6475aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                // If the specified rotation is the same or is counter-clockwise adjacent
6485aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                // to the current rotation, then we set a lower bound on the orientation angle.
649c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown                // For example, if currentRotation is ROTATION_0 and proposed is ROTATION_90,
6504519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                // then we want to check orientationAngle > 45 + GAP / 2.
6515aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                if (rotation == currentRotation
6525aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                        || rotation == (currentRotation + 1) % 4) {
6535aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                    int lowerBound = rotation * 90 - 45
6544519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                            + ADJACENT_ORIENTATION_ANGLE_GAP / 2;
6555aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                    if (rotation == 0) {
6564519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                        if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) {
6574519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                            return false;
6584519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                        }
6594519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                    } else {
6604519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                        if (orientationAngle < lowerBound) {
6614519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                            return false;
6624519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                        }
6634519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                    }
6645f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard                }
6655f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard
6665aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                // If the specified rotation is the same or is clockwise adjacent,
6674519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                // then we set an upper bound on the orientation angle.
6685aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                // For example, if currentRotation is ROTATION_0 and rotation is ROTATION_270,
6694519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                // then we want to check orientationAngle < 315 - GAP / 2.
6705aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                if (rotation == currentRotation
6715aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                        || rotation == (currentRotation + 3) % 4) {
6725aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                    int upperBound = rotation * 90 + 45
6734519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                            - ADJACENT_ORIENTATION_ANGLE_GAP / 2;
6745aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                    if (rotation == 0) {
6754519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                        if (orientationAngle <= 45 && orientationAngle > upperBound) {
6764519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                            return false;
6774519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                        }
6784519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                    } else {
6794519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                        if (orientationAngle > upperBound) {
6804519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                            return false;
6814519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                        }
6824519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown                    }
6835f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard                }
6845f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard            }
6854519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown            return true;
6865f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard        }
6875f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard
6885aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        /**
6895aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown         * Returns true if the predicted rotation is ready to be advertised as a
6905aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown         * proposed rotation.
6915aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown         */
692eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        private boolean isPredictedRotationAcceptableLocked(long now) {
6935aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            // The predicted rotation must have settled long enough.
6945aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            if (now < mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) {
6955aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                return false;
6965aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            }
6975aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
6985aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            // The last flat state (time since picked up) must have been sufficiently long ago.
6995aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            if (now < mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) {
7005aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                return false;
7015aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            }
7025f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard
7035aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            // The last swing state (time since last movement to put down) must have been
7045aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            // sufficiently long ago.
7055aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            if (now < mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) {
7065aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                return false;
707c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown            }
708c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown
709daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown            // The last acceleration state must have been sufficiently long ago.
710daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown            if (now < mAccelerationTimestampNanos
711daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown                    + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) {
712daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown                return false;
713daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown            }
714daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown
7155aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            // Looks good!
7165aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            return true;
7175aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        }
7185aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
719eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        private void resetLocked() {
7205aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            mLastFilteredTimestampNanos = Long.MIN_VALUE;
7215aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            mProposedRotation = -1;
7225aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            mFlatTimestampNanos = Long.MIN_VALUE;
723600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            mFlat = false;
7245aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            mSwingTimestampNanos = Long.MIN_VALUE;
725600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            mSwinging = false;
726daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown            mAccelerationTimestampNanos = Long.MIN_VALUE;
727600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            mAccelerating = false;
728600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            mOverhead = false;
729eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            clearPredictedRotationLocked();
730eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            clearTiltHistoryLocked();
7315aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        }
7325aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
733eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        private void clearPredictedRotationLocked() {
7345aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            mPredictedRotation = -1;
7355aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            mPredictedRotationTimestampNanos = Long.MIN_VALUE;
7365aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        }
7375aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
738eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        private void updatePredictedRotationLocked(long now, int rotation) {
7395aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            if (mPredictedRotation != rotation) {
7405aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                mPredictedRotation = rotation;
7415aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                mPredictedRotationTimestampNanos = now;
742c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown            }
7435aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        }
744c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown
745eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        private boolean isAcceleratingLocked(float magnitude) {
746daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown            return magnitude < MIN_ACCELERATION_MAGNITUDE
747daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown                    || magnitude > MAX_ACCELERATION_MAGNITUDE;
748daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown        }
749daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown
750eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        private void clearTiltHistoryLocked() {
7515aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            mTiltHistoryTimestampNanos[0] = Long.MIN_VALUE;
7525aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            mTiltHistoryIndex = 1;
7535aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        }
7545aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
755eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        private void addTiltHistoryEntryLocked(long now, float tilt) {
7565aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            mTiltHistory[mTiltHistoryIndex] = tilt;
7575aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            mTiltHistoryTimestampNanos[mTiltHistoryIndex] = now;
7585aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            mTiltHistoryIndex = (mTiltHistoryIndex + 1) % TILT_HISTORY_SIZE;
7595aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            mTiltHistoryTimestampNanos[mTiltHistoryIndex] = Long.MIN_VALUE;
7605aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        }
7615aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
762eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        private boolean isFlatLocked(long now) {
763eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndexLocked(i)) >= 0; ) {
7645aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                if (mTiltHistory[i] < FLAT_ANGLE) {
765c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown                    break;
766c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown                }
7675aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                if (mTiltHistoryTimestampNanos[i] + FLAT_TIME_NANOS <= now) {
7685aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                    // Tilt has remained greater than FLAT_TILT_ANGLE for FLAT_TIME_NANOS.
7695aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                    return true;
770c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown                }
7715aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            }
7725aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            return false;
7735aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        }
7745aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
775eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        private boolean isSwingingLocked(long now, float tilt) {
776eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner            for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndexLocked(i)) >= 0; ) {
7775aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                if (mTiltHistoryTimestampNanos[i] + SWING_TIME_NANOS < now) {
778c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown                    break;
779c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown                }
7805aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                if (mTiltHistory[i] + SWING_AWAY_ANGLE_DELTA <= tilt) {
7815aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                    // Tilted away by SWING_AWAY_ANGLE_DELTA within SWING_TIME_NANOS.
7825aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown                    return true;
783c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown                }
784c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown            }
7855aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            return false;
786c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown        }
787c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown
788eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        private int nextTiltHistoryIndexLocked(int index) {
7895aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            index = (index == 0 ? TILT_HISTORY_SIZE : index) - 1;
7905aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            return mTiltHistoryTimestampNanos[index] != Long.MIN_VALUE ? index : -1;
7915aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown        }
7925aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown
793600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown        private float getLastTiltLocked() {
794600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            int index = nextTiltHistoryIndexLocked(mTiltHistoryIndex);
795600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown            return index >= 0 ? mTiltHistory[index] : Float.NaN;
796600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown        }
797600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown
798eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner        private float remainingMS(long now, long until) {
7995aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown            return now >= until ? 0 : (until - now) * 0.000001f;
8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
803