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