13b971598ed28d45c176e3f9b076fc743a406296dJeff Brown/* 23b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * Copyright (C) 2013 The Android Open Source Project 33b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * 43b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * Licensed under the Apache License, Version 2.0 (the "License"); 53b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * you may not use this file except in compliance with the License. 63b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * You may obtain a copy of the License at 73b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * 83b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * http://www.apache.org/licenses/LICENSE-2.0 93b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * 103b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * Unless required by applicable law or agreed to in writing, software 113b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * distributed under the License is distributed on an "AS IS" BASIS, 123b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * See the License for the specific language governing permissions and 143b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * limitations under the License. 153b971598ed28d45c176e3f9b076fc743a406296dJeff Brown */ 163b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 173b971598ed28d45c176e3f9b076fc743a406296dJeff Brownpackage com.android.server.power; 183b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 193b971598ed28d45c176e3f9b076fc743a406296dJeff Brownimport android.hardware.Sensor; 203b971598ed28d45c176e3f9b076fc743a406296dJeff Brownimport android.hardware.SensorEvent; 213b971598ed28d45c176e3f9b076fc743a406296dJeff Brownimport android.hardware.SensorEventListener; 223b971598ed28d45c176e3f9b076fc743a406296dJeff Brownimport android.hardware.SensorManager; 233b971598ed28d45c176e3f9b076fc743a406296dJeff Brownimport android.os.BatteryManager; 24dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brownimport android.os.Handler; 25dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brownimport android.os.Message; 2620e287534396655af0d5912d8b272070ad24a93aJeff Brownimport android.os.SystemClock; 273b971598ed28d45c176e3f9b076fc743a406296dJeff Brownimport android.util.Slog; 28dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brownimport android.util.TimeUtils; 293b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 303b971598ed28d45c176e3f9b076fc743a406296dJeff Brownimport java.io.PrintWriter; 313b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 323b971598ed28d45c176e3f9b076fc743a406296dJeff Brown/** 333b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * Implements heuristics to detect docking or undocking from a wireless charger. 343b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * <p> 353b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * Some devices have wireless charging circuits that are unable to detect when the 363b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * device is resting on a wireless charger except when the device is actually 373b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * receiving power from the charger. The device may stop receiving power 383b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * if the battery is already nearly full or if it is too hot. As a result, we cannot 393b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * always rely on the battery service wireless plug signal to accurately indicate 403b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * whether the device has been docked or undocked from a wireless charger. 413b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p><p> 423b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * This is a problem because the power manager typically wakes up the screen and 433b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * plays a tone when the device is docked in a wireless charger. It is important 443b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * for the system to suppress spurious docking and undocking signals because they 453b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * can be intrusive for the user (especially if they cause a tone to be played 463b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * late at night for no apparent reason). 473b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p><p> 483b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * To avoid spurious signals, we apply some special policies to wireless chargers. 493b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p><p> 503b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * 1. Don't wake the device when undocked from the wireless charger because 513b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * it might be that the device is still resting on the wireless charger 523b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * but is not receiving power anymore because the battery is full. 533b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * Ideally we would wake the device if we could be certain that the user had 543b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * picked it up from the wireless charger but due to hardware limitations we 553b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * must be more conservative. 563b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p><p> 573b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * 2. Don't wake the device when docked on a wireless charger if the 583b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * battery already appears to be mostly full. This situation may indicate 593b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * that the device was resting on the charger the whole time and simply 603b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * wasn't receiving power because the battery was already full. We can't tell 613b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * whether the device was just placed on the charger or whether it has 623b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * been there for half of the night slowly discharging until it reached 633b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * the point where it needed to start charging again. So we suppress docking 643b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * signals that occur when the battery level is above a given threshold. 653b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p><p> 663b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * 3. Don't wake the device when docked on a wireless charger if it does 673b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * not appear to have moved since it was last undocked because it may 683b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * be that the prior undocking signal was spurious. We use the gravity 693b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * sensor to detect this case. 703b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p> 713b971598ed28d45c176e3f9b076fc743a406296dJeff Brown */ 723b971598ed28d45c176e3f9b076fc743a406296dJeff Brownfinal class WirelessChargerDetector { 733b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private static final String TAG = "WirelessChargerDetector"; 743b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private static final boolean DEBUG = false; 753b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 763b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The minimum amount of time to spend watching the sensor before making 773b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // a determination of whether movement occurred. 78dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown private static final long SETTLE_TIME_MILLIS = 800; 79dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown 80dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown // The sensor sampling interval. 81dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown private static final int SAMPLING_INTERVAL_MILLIS = 50; 823b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 833b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The minimum number of samples that must be collected. 843b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private static final int MIN_SAMPLES = 3; 853b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 863b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Upper bound on the battery charge percentage in order to consider turning 873b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // the screen on when the device starts charging wirelessly. 883b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private static final int WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT = 95; 893b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 903b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // To detect movement, we compute the angle between the gravity vector 913b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // at rest and the current gravity vector. This field specifies the 923b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // cosine of the maximum angle variance that we tolerate while at rest. 933b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private static final double MOVEMENT_ANGLE_COS_THRESHOLD = Math.cos(5 * Math.PI / 180); 943b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 953b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Sanity thresholds for the gravity vector. 963b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private static final double MIN_GRAVITY = SensorManager.GRAVITY_EARTH - 1.0f; 973b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private static final double MAX_GRAVITY = SensorManager.GRAVITY_EARTH + 1.0f; 983b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 993b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private final Object mLock = new Object(); 1003b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1013b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private final SensorManager mSensorManager; 1023b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private final SuspendBlocker mSuspendBlocker; 103dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown private final Handler mHandler; 1043b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1053b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The gravity sensor, or null if none. 1063b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private Sensor mGravitySensor; 1073b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1083b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Previously observed wireless power state. 1093b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private boolean mPoweredWirelessly; 1103b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1113b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // True if the device is thought to be at rest on a wireless charger. 1123b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private boolean mAtRest; 1133b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1143b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The gravity vector most recently observed while at rest. 1153b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private float mRestX, mRestY, mRestZ; 1163b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1173b971598ed28d45c176e3f9b076fc743a406296dJeff Brown /* These properties are only meaningful while detection is in progress. */ 1183b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1193b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // True if detection is in progress. 1203b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The suspend blocker is held while this is the case. 1213b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private boolean mDetectionInProgress; 1223b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 123dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown // The time when detection was last performed. 124dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown private long mDetectionStartTime; 125dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown 1263b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // True if the rest position should be updated if at rest. 1273b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Otherwise, the current rest position is simply checked and cleared if movement 1283b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // is detected but no new rest position is stored. 1293b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private boolean mMustUpdateRestPosition; 1303b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1313b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The total number of samples collected. 1323b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private int mTotalSamples; 1333b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1343b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The number of samples collected that showed evidence of not being at rest. 1353b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private int mMovingSamples; 1363b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 137dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown // The value of the first sample that was collected. 1383b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private float mFirstSampleX, mFirstSampleY, mFirstSampleZ; 1393b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 140dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown // The value of the last sample that was collected. 14120e287534396655af0d5912d8b272070ad24a93aJeff Brown private float mLastSampleX, mLastSampleY, mLastSampleZ; 14220e287534396655af0d5912d8b272070ad24a93aJeff Brown 1433b971598ed28d45c176e3f9b076fc743a406296dJeff Brown public WirelessChargerDetector(SensorManager sensorManager, 144dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown SuspendBlocker suspendBlocker, Handler handler) { 1453b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mSensorManager = sensorManager; 1463b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mSuspendBlocker = suspendBlocker; 147dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mHandler = handler; 1483b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1493b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mGravitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY); 1503b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 1513b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1523b971598ed28d45c176e3f9b076fc743a406296dJeff Brown public void dump(PrintWriter pw) { 1533b971598ed28d45c176e3f9b076fc743a406296dJeff Brown synchronized (mLock) { 1543b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(); 1553b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println("Wireless Charger Detector State:"); 1563b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mGravitySensor=" + mGravitySensor); 1573b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mPoweredWirelessly=" + mPoweredWirelessly); 1583b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mAtRest=" + mAtRest); 1593b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mRestX=" + mRestX + ", mRestY=" + mRestY + ", mRestZ=" + mRestZ); 1603b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mDetectionInProgress=" + mDetectionInProgress); 161dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown pw.println(" mDetectionStartTime=" + (mDetectionStartTime == 0 ? "0 (never)" 162dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown : TimeUtils.formatUptime(mDetectionStartTime))); 1633b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mMustUpdateRestPosition=" + mMustUpdateRestPosition); 1643b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mTotalSamples=" + mTotalSamples); 1653b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mMovingSamples=" + mMovingSamples); 1663b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mFirstSampleX=" + mFirstSampleX 1673b971598ed28d45c176e3f9b076fc743a406296dJeff Brown + ", mFirstSampleY=" + mFirstSampleY + ", mFirstSampleZ=" + mFirstSampleZ); 16820e287534396655af0d5912d8b272070ad24a93aJeff Brown pw.println(" mLastSampleX=" + mLastSampleX 16920e287534396655af0d5912d8b272070ad24a93aJeff Brown + ", mLastSampleY=" + mLastSampleY + ", mLastSampleZ=" + mLastSampleZ); 1703b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 1713b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 1723b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1733b971598ed28d45c176e3f9b076fc743a406296dJeff Brown /** 1743b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * Updates the charging state and returns true if docking was detected. 1753b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * 1763b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * @param isPowered True if the device is powered. 1773b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * @param plugType The current plug type. 1783b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * @param batteryLevel The current battery level. 1793b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * @return True if the device is determined to have just been docked on a wireless 1803b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * charger, after suppressing spurious docking or undocking signals. 1813b971598ed28d45c176e3f9b076fc743a406296dJeff Brown */ 1823b971598ed28d45c176e3f9b076fc743a406296dJeff Brown public boolean update(boolean isPowered, int plugType, int batteryLevel) { 1833b971598ed28d45c176e3f9b076fc743a406296dJeff Brown synchronized (mLock) { 1843b971598ed28d45c176e3f9b076fc743a406296dJeff Brown final boolean wasPoweredWirelessly = mPoweredWirelessly; 1853b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1863b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (isPowered && plugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) { 1873b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The device is receiving power from the wireless charger. 1883b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Update the rest position asynchronously. 1893b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mPoweredWirelessly = true; 1903b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mMustUpdateRestPosition = true; 1913b971598ed28d45c176e3f9b076fc743a406296dJeff Brown startDetectionLocked(); 1923b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } else { 1933b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The device may or may not be on the wireless charger depending on whether 1943b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // the unplug signal that we received was spurious. 1953b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mPoweredWirelessly = false; 1963b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (mAtRest) { 1973b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (plugType != 0 && plugType != BatteryManager.BATTERY_PLUGGED_WIRELESS) { 1983b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The device was plugged into a new non-wireless power source. 1993b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // It's safe to assume that it is no longer on the wireless charger. 2003b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mMustUpdateRestPosition = false; 2013b971598ed28d45c176e3f9b076fc743a406296dJeff Brown clearAtRestLocked(); 2023b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } else { 2033b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The device may still be on the wireless charger but we don't know. 2043b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Check whether the device has remained at rest on the charger 2053b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // so that we will know to ignore the next wireless plug event 2063b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // if needed. 2073b971598ed28d45c176e3f9b076fc743a406296dJeff Brown startDetectionLocked(); 2083b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2093b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2103b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2113b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 2123b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Report that the device has been docked only if the device just started 2133b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // receiving power wirelessly, has a high enough battery level that we 2143b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // can be assured that charging was not delayed due to the battery previously 2153b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // having been full, and the device is not known to already be at rest 2163b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // on the wireless charger from earlier. 2173b971598ed28d45c176e3f9b076fc743a406296dJeff Brown return mPoweredWirelessly && !wasPoweredWirelessly 2183b971598ed28d45c176e3f9b076fc743a406296dJeff Brown && batteryLevel < WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT 2193b971598ed28d45c176e3f9b076fc743a406296dJeff Brown && !mAtRest; 2203b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2213b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2223b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 2233b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private void startDetectionLocked() { 2243b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (!mDetectionInProgress && mGravitySensor != null) { 2253b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (mSensorManager.registerListener(mListener, mGravitySensor, 226dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown SAMPLING_INTERVAL_MILLIS * 1000)) { 2273b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mSuspendBlocker.acquire(); 2283b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mDetectionInProgress = true; 229dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mDetectionStartTime = SystemClock.uptimeMillis(); 2303b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mTotalSamples = 0; 2313b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mMovingSamples = 0; 232dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown 233dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown Message msg = Message.obtain(mHandler, mSensorTimeout); 234dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown msg.setAsynchronous(true); 235dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mHandler.sendMessageDelayed(msg, SETTLE_TIME_MILLIS); 2363b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2373b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2383b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2393b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 240dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown private void finishDetectionLocked() { 241dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown if (mDetectionInProgress) { 242dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mSensorManager.unregisterListener(mListener); 243dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mHandler.removeCallbacks(mSensorTimeout); 244dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown 245dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown if (mMustUpdateRestPosition) { 246dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown clearAtRestLocked(); 247dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown if (mTotalSamples < MIN_SAMPLES) { 248dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown Slog.w(TAG, "Wireless charger detector is broken. Only received " 249dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown + mTotalSamples + " samples from the gravity sensor but we " 250dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown + "need at least " + MIN_SAMPLES + " and we expect to see " 251dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown + "about " + SETTLE_TIME_MILLIS / SAMPLING_INTERVAL_MILLIS 252dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown + " on average."); 253dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown } else if (mMovingSamples == 0) { 254dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mAtRest = true; 255dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mRestX = mLastSampleX; 256dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mRestY = mLastSampleY; 257dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mRestZ = mLastSampleZ; 258dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown } 259dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mMustUpdateRestPosition = false; 2603b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2613b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 262dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown if (DEBUG) { 263dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown Slog.d(TAG, "New state: mAtRest=" + mAtRest 264dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown + ", mRestX=" + mRestX + ", mRestY=" + mRestY + ", mRestZ=" + mRestZ 265dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown + ", mTotalSamples=" + mTotalSamples 266dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown + ", mMovingSamples=" + mMovingSamples); 267dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown } 268dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown 269dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mDetectionInProgress = false; 270dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mSuspendBlocker.release(); 271dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown } 272dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown } 273dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown 274dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown private void processSampleLocked(float x, float y, float z) { 275dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown if (mDetectionInProgress) { 27620e287534396655af0d5912d8b272070ad24a93aJeff Brown mLastSampleX = x; 27720e287534396655af0d5912d8b272070ad24a93aJeff Brown mLastSampleY = y; 27820e287534396655af0d5912d8b272070ad24a93aJeff Brown mLastSampleZ = z; 27920e287534396655af0d5912d8b272070ad24a93aJeff Brown 2803b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mTotalSamples += 1; 2813b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (mTotalSamples == 1) { 2823b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Save information about the first sample collected. 2833b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mFirstSampleX = x; 2843b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mFirstSampleY = y; 2853b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mFirstSampleZ = z; 2863b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } else { 2873b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Determine whether movement has occurred relative to the first sample. 2883b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (hasMoved(mFirstSampleX, mFirstSampleY, mFirstSampleZ, x, y, z)) { 2893b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mMovingSamples += 1; 2903b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2913b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2923b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 2933b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Clear the at rest flag if movement has occurred relative to the rest sample. 2943b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (mAtRest && hasMoved(mRestX, mRestY, mRestZ, x, y, z)) { 2953b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (DEBUG) { 2963b971598ed28d45c176e3f9b076fc743a406296dJeff Brown Slog.d(TAG, "No longer at rest: " 2973b971598ed28d45c176e3f9b076fc743a406296dJeff Brown + "mRestX=" + mRestX + ", mRestY=" + mRestY + ", mRestZ=" + mRestZ 2983b971598ed28d45c176e3f9b076fc743a406296dJeff Brown + ", x=" + x + ", y=" + y + ", z=" + z); 2993b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3003b971598ed28d45c176e3f9b076fc743a406296dJeff Brown clearAtRestLocked(); 3013b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3023b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3033b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3043b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 3053b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private void clearAtRestLocked() { 3063b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mAtRest = false; 3073b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mRestX = 0; 3083b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mRestY = 0; 3093b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mRestZ = 0; 3103b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3113b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 3123b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private static boolean hasMoved(float x1, float y1, float z1, 3133b971598ed28d45c176e3f9b076fc743a406296dJeff Brown float x2, float y2, float z2) { 3143b971598ed28d45c176e3f9b076fc743a406296dJeff Brown final double dotProduct = (x1 * x2) + (y1 * y2) + (z1 * z2); 3153b971598ed28d45c176e3f9b076fc743a406296dJeff Brown final double mag1 = Math.sqrt((x1 * x1) + (y1 * y1) + (z1 * z1)); 3163b971598ed28d45c176e3f9b076fc743a406296dJeff Brown final double mag2 = Math.sqrt((x2 * x2) + (y2 * y2) + (z2 * z2)); 3173b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (mag1 < MIN_GRAVITY || mag1 > MAX_GRAVITY 3183b971598ed28d45c176e3f9b076fc743a406296dJeff Brown || mag2 < MIN_GRAVITY || mag2 > MAX_GRAVITY) { 3193b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (DEBUG) { 3203b971598ed28d45c176e3f9b076fc743a406296dJeff Brown Slog.d(TAG, "Weird gravity vector: mag1=" + mag1 + ", mag2=" + mag2); 3213b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3223b971598ed28d45c176e3f9b076fc743a406296dJeff Brown return true; 3233b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3243b971598ed28d45c176e3f9b076fc743a406296dJeff Brown final boolean moved = (dotProduct < mag1 * mag2 * MOVEMENT_ANGLE_COS_THRESHOLD); 3253b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (DEBUG) { 3263b971598ed28d45c176e3f9b076fc743a406296dJeff Brown Slog.d(TAG, "Check: moved=" + moved 3273b971598ed28d45c176e3f9b076fc743a406296dJeff Brown + ", x1=" + x1 + ", y1=" + y1 + ", z1=" + z1 3283b971598ed28d45c176e3f9b076fc743a406296dJeff Brown + ", x2=" + x2 + ", y2=" + y2 + ", z2=" + z2 3293b971598ed28d45c176e3f9b076fc743a406296dJeff Brown + ", angle=" + (Math.acos(dotProduct / mag1 / mag2) * 180 / Math.PI) 3303b971598ed28d45c176e3f9b076fc743a406296dJeff Brown + ", dotProduct=" + dotProduct 3313b971598ed28d45c176e3f9b076fc743a406296dJeff Brown + ", mag1=" + mag1 + ", mag2=" + mag2); 3323b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3333b971598ed28d45c176e3f9b076fc743a406296dJeff Brown return moved; 3343b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3353b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 3363b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private final SensorEventListener mListener = new SensorEventListener() { 3373b971598ed28d45c176e3f9b076fc743a406296dJeff Brown @Override 3383b971598ed28d45c176e3f9b076fc743a406296dJeff Brown public void onSensorChanged(SensorEvent event) { 339dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown synchronized (mLock) { 340dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown processSampleLocked(event.values[0], event.values[1], event.values[2]); 341dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown } 3423b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3433b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 3443b971598ed28d45c176e3f9b076fc743a406296dJeff Brown @Override 3453b971598ed28d45c176e3f9b076fc743a406296dJeff Brown public void onAccuracyChanged(Sensor sensor, int accuracy) { 3463b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3473b971598ed28d45c176e3f9b076fc743a406296dJeff Brown }; 348dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown 349dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown private final Runnable mSensorTimeout = new Runnable() { 350dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown @Override 351dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown public void run() { 352dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown synchronized (mLock) { 353dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown finishDetectionLocked(); 354dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown } 355dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown } 356dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown }; 3573b971598ed28d45c176e3f9b076fc743a406296dJeff Brown} 358