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;
29958d0a503699840f8f4b85cafa62f3793c89ac73Netta Pimport android.util.proto.ProtoOutputStream;
303b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
313b971598ed28d45c176e3f9b076fc743a406296dJeff Brownimport java.io.PrintWriter;
323b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
333b971598ed28d45c176e3f9b076fc743a406296dJeff Brown/**
343b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * Implements heuristics to detect docking or undocking from a wireless charger.
353b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * <p>
363b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * Some devices have wireless charging circuits that are unable to detect when the
373b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * device is resting on a wireless charger except when the device is actually
383b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * receiving power from the charger.  The device may stop receiving power
393b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * if the battery is already nearly full or if it is too hot.  As a result, we cannot
403b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * always rely on the battery service wireless plug signal to accurately indicate
413b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * whether the device has been docked or undocked from a wireless charger.
423b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p><p>
433b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * This is a problem because the power manager typically wakes up the screen and
443b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * plays a tone when the device is docked in a wireless charger.  It is important
453b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * for the system to suppress spurious docking and undocking signals because they
463b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * can be intrusive for the user (especially if they cause a tone to be played
473b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * late at night for no apparent reason).
483b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p><p>
493b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * To avoid spurious signals, we apply some special policies to wireless chargers.
503b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p><p>
513b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * 1. Don't wake the device when undocked from the wireless charger because
523b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * it might be that the device is still resting on the wireless charger
533b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * but is not receiving power anymore because the battery is full.
543b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * Ideally we would wake the device if we could be certain that the user had
553b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * picked it up from the wireless charger but due to hardware limitations we
563b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * must be more conservative.
573b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p><p>
583b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * 2. Don't wake the device when docked on a wireless charger if the
593b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * battery already appears to be mostly full.  This situation may indicate
603b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * that the device was resting on the charger the whole time and simply
613b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * wasn't receiving power because the battery was already full.  We can't tell
623b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * whether the device was just placed on the charger or whether it has
633b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * been there for half of the night slowly discharging until it reached
643b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * the point where it needed to start charging again.  So we suppress docking
653b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * signals that occur when the battery level is above a given threshold.
663b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p><p>
673b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * 3. Don't wake the device when docked on a wireless charger if it does
683b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * not appear to have moved since it was last undocked because it may
693b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * be that the prior undocking signal was spurious.  We use the gravity
703b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * sensor to detect this case.
713b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p>
723b971598ed28d45c176e3f9b076fc743a406296dJeff Brown */
733b971598ed28d45c176e3f9b076fc743a406296dJeff Brownfinal class WirelessChargerDetector {
743b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private static final String TAG = "WirelessChargerDetector";
753b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private static final boolean DEBUG = false;
763b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
773b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // The minimum amount of time to spend watching the sensor before making
783b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // a determination of whether movement occurred.
79dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown    private static final long SETTLE_TIME_MILLIS = 800;
80dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown
81dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown    // The sensor sampling interval.
82dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown    private static final int SAMPLING_INTERVAL_MILLIS = 50;
833b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
843b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // The minimum number of samples that must be collected.
853b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private static final int MIN_SAMPLES = 3;
863b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
873b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // To detect movement, we compute the angle between the gravity vector
883b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // at rest and the current gravity vector.  This field specifies the
893b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // cosine of the maximum angle variance that we tolerate while at rest.
903b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private static final double MOVEMENT_ANGLE_COS_THRESHOLD = Math.cos(5 * Math.PI / 180);
913b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
923b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // Sanity thresholds for the gravity vector.
933b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private static final double MIN_GRAVITY = SensorManager.GRAVITY_EARTH - 1.0f;
943b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private static final double MAX_GRAVITY = SensorManager.GRAVITY_EARTH + 1.0f;
953b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
963b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private final Object mLock = new Object();
973b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
983b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private final SensorManager mSensorManager;
993b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private final SuspendBlocker mSuspendBlocker;
100dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown    private final Handler mHandler;
1013b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
1023b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // The gravity sensor, or null if none.
1033b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private Sensor mGravitySensor;
1043b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
1053b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // Previously observed wireless power state.
1063b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private boolean mPoweredWirelessly;
1073b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
1083b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // True if the device is thought to be at rest on a wireless charger.
1093b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private boolean mAtRest;
1103b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
1113b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // The gravity vector most recently observed while at rest.
1123b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private float mRestX, mRestY, mRestZ;
1133b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
1143b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    /* These properties are only meaningful while detection is in progress. */
1153b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
1163b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // True if detection is in progress.
1173b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // The suspend blocker is held while this is the case.
1183b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private boolean mDetectionInProgress;
1193b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
120dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown    // The time when detection was last performed.
121dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown    private long mDetectionStartTime;
122dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown
1233b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // True if the rest position should be updated if at rest.
1243b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // Otherwise, the current rest position is simply checked and cleared if movement
1253b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // is detected but no new rest position is stored.
1263b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private boolean mMustUpdateRestPosition;
1273b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
1283b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // The total number of samples collected.
1293b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private int mTotalSamples;
1303b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
1313b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    // The number of samples collected that showed evidence of not being at rest.
1323b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private int mMovingSamples;
1333b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
134dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown    // The value of the first sample that was collected.
1353b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private float mFirstSampleX, mFirstSampleY, mFirstSampleZ;
1363b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
137dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown    // The value of the last sample that was collected.
13820e287534396655af0d5912d8b272070ad24a93aJeff Brown    private float mLastSampleX, mLastSampleY, mLastSampleZ;
13920e287534396655af0d5912d8b272070ad24a93aJeff Brown
1403b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    public WirelessChargerDetector(SensorManager sensorManager,
141dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown            SuspendBlocker suspendBlocker, Handler handler) {
1423b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        mSensorManager = sensorManager;
1433b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        mSuspendBlocker = suspendBlocker;
144dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown        mHandler = handler;
1453b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
1463b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        mGravitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
1473b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    }
1483b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
1493b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    public void dump(PrintWriter pw) {
1503b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        synchronized (mLock) {
1513b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            pw.println();
1523b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            pw.println("Wireless Charger Detector State:");
1533b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            pw.println("  mGravitySensor=" + mGravitySensor);
1543b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            pw.println("  mPoweredWirelessly=" + mPoweredWirelessly);
1553b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            pw.println("  mAtRest=" + mAtRest);
1563b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            pw.println("  mRestX=" + mRestX + ", mRestY=" + mRestY + ", mRestZ=" + mRestZ);
1573b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            pw.println("  mDetectionInProgress=" + mDetectionInProgress);
158dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown            pw.println("  mDetectionStartTime=" + (mDetectionStartTime == 0 ? "0 (never)"
159dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                    : TimeUtils.formatUptime(mDetectionStartTime)));
1603b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            pw.println("  mMustUpdateRestPosition=" + mMustUpdateRestPosition);
1613b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            pw.println("  mTotalSamples=" + mTotalSamples);
1623b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            pw.println("  mMovingSamples=" + mMovingSamples);
1633b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            pw.println("  mFirstSampleX=" + mFirstSampleX
1643b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                    + ", mFirstSampleY=" + mFirstSampleY + ", mFirstSampleZ=" + mFirstSampleZ);
16520e287534396655af0d5912d8b272070ad24a93aJeff Brown            pw.println("  mLastSampleX=" + mLastSampleX
16620e287534396655af0d5912d8b272070ad24a93aJeff Brown                    + ", mLastSampleY=" + mLastSampleY + ", mLastSampleZ=" + mLastSampleZ);
1673b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        }
1683b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    }
1693b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
170958d0a503699840f8f4b85cafa62f3793c89ac73Netta P    public void writeToProto(ProtoOutputStream proto, long fieldId) {
171958d0a503699840f8f4b85cafa62f3793c89ac73Netta P        final long wcdToken = proto.start(fieldId);
172958d0a503699840f8f4b85cafa62f3793c89ac73Netta P        synchronized (mLock) {
173958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.write(WirelessChargerDetectorProto.IS_POWERED_WIRELESSLY, mPoweredWirelessly);
174958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.write(WirelessChargerDetectorProto.IS_AT_REST, mAtRest);
175958d0a503699840f8f4b85cafa62f3793c89ac73Netta P
176958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            final long restVectorToken = proto.start(WirelessChargerDetectorProto.REST);
177958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.write(WirelessChargerDetectorProto.VectorProto.X, mRestX);
178958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.write(WirelessChargerDetectorProto.VectorProto.Y, mRestY);
179958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.write(WirelessChargerDetectorProto.VectorProto.Z, mRestZ);
180958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.end(restVectorToken);
181958d0a503699840f8f4b85cafa62f3793c89ac73Netta P
182958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.write(
183958d0a503699840f8f4b85cafa62f3793c89ac73Netta P                    WirelessChargerDetectorProto.IS_DETECTION_IN_PROGRESS, mDetectionInProgress);
184958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.write(WirelessChargerDetectorProto.DETECTION_START_TIME_MS, mDetectionStartTime);
185958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.write(
186958d0a503699840f8f4b85cafa62f3793c89ac73Netta P                    WirelessChargerDetectorProto.IS_MUST_UPDATE_REST_POSITION,
187958d0a503699840f8f4b85cafa62f3793c89ac73Netta P                    mMustUpdateRestPosition);
188958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.write(WirelessChargerDetectorProto.TOTAL_SAMPLES, mTotalSamples);
189958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.write(WirelessChargerDetectorProto.MOVING_SAMPLES, mMovingSamples);
190958d0a503699840f8f4b85cafa62f3793c89ac73Netta P
191958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            final long firstSampleVectorToken =
192958d0a503699840f8f4b85cafa62f3793c89ac73Netta P                    proto.start(WirelessChargerDetectorProto.FIRST_SAMPLE);
193958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.write(WirelessChargerDetectorProto.VectorProto.X, mFirstSampleX);
194958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.write(WirelessChargerDetectorProto.VectorProto.Y, mFirstSampleY);
195958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.write(WirelessChargerDetectorProto.VectorProto.Z, mFirstSampleZ);
196958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.end(firstSampleVectorToken);
197958d0a503699840f8f4b85cafa62f3793c89ac73Netta P
198958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            final long lastSampleVectorToken =
199958d0a503699840f8f4b85cafa62f3793c89ac73Netta P                    proto.start(WirelessChargerDetectorProto.LAST_SAMPLE);
200958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.write(WirelessChargerDetectorProto.VectorProto.X, mLastSampleX);
201958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.write(WirelessChargerDetectorProto.VectorProto.Y, mLastSampleY);
202958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.write(WirelessChargerDetectorProto.VectorProto.Z, mLastSampleZ);
203958d0a503699840f8f4b85cafa62f3793c89ac73Netta P            proto.end(lastSampleVectorToken);
204958d0a503699840f8f4b85cafa62f3793c89ac73Netta P        }
205958d0a503699840f8f4b85cafa62f3793c89ac73Netta P        proto.end(wcdToken);
206958d0a503699840f8f4b85cafa62f3793c89ac73Netta P    }
207958d0a503699840f8f4b85cafa62f3793c89ac73Netta P
2083b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    /**
2093b971598ed28d45c176e3f9b076fc743a406296dJeff Brown     * Updates the charging state and returns true if docking was detected.
2103b971598ed28d45c176e3f9b076fc743a406296dJeff Brown     *
2113b971598ed28d45c176e3f9b076fc743a406296dJeff Brown     * @param isPowered True if the device is powered.
2123b971598ed28d45c176e3f9b076fc743a406296dJeff Brown     * @param plugType The current plug type.
2133b971598ed28d45c176e3f9b076fc743a406296dJeff Brown     * @return True if the device is determined to have just been docked on a wireless
2143b971598ed28d45c176e3f9b076fc743a406296dJeff Brown     * charger, after suppressing spurious docking or undocking signals.
2153b971598ed28d45c176e3f9b076fc743a406296dJeff Brown     */
216fcaab29a663cc2b18c626464e1ceae6b44e51be6Beverly    public boolean update(boolean isPowered, int plugType) {
2173b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        synchronized (mLock) {
2183b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            final boolean wasPoweredWirelessly = mPoweredWirelessly;
2193b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
2203b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            if (isPowered && plugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
2213b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                // The device is receiving power from the wireless charger.
2223b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                // Update the rest position asynchronously.
2233b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                mPoweredWirelessly = true;
2243b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                mMustUpdateRestPosition = true;
2253b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                startDetectionLocked();
2263b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            } else {
2273b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                // The device may or may not be on the wireless charger depending on whether
2283b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                // the unplug signal that we received was spurious.
2293b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                mPoweredWirelessly = false;
2303b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                if (mAtRest) {
2313b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                    if (plugType != 0 && plugType != BatteryManager.BATTERY_PLUGGED_WIRELESS) {
2323b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                        // The device was plugged into a new non-wireless power source.
2333b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                        // It's safe to assume that it is no longer on the wireless charger.
2343b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                        mMustUpdateRestPosition = false;
2353b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                        clearAtRestLocked();
2363b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                    } else {
2373b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                        // The device may still be on the wireless charger but we don't know.
2383b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                        // Check whether the device has remained at rest on the charger
2393b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                        // so that we will know to ignore the next wireless plug event
2403b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                        // if needed.
2413b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                        startDetectionLocked();
2423b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                    }
2433b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                }
2443b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            }
2453b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
2463b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            // Report that the device has been docked only if the device just started
247fcaab29a663cc2b18c626464e1ceae6b44e51be6Beverly            // receiving power wirelessly and the device is not known to already be at rest
2483b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            // on the wireless charger from earlier.
249fcaab29a663cc2b18c626464e1ceae6b44e51be6Beverly            return mPoweredWirelessly && !wasPoweredWirelessly && !mAtRest;
2503b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        }
2513b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    }
2523b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
2533b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private void startDetectionLocked() {
2543b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        if (!mDetectionInProgress && mGravitySensor != null) {
2553b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            if (mSensorManager.registerListener(mListener, mGravitySensor,
256dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                    SAMPLING_INTERVAL_MILLIS * 1000)) {
2573b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                mSuspendBlocker.acquire();
2583b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                mDetectionInProgress = true;
259dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                mDetectionStartTime = SystemClock.uptimeMillis();
2603b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                mTotalSamples = 0;
2613b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                mMovingSamples = 0;
262dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown
263dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                Message msg = Message.obtain(mHandler, mSensorTimeout);
264dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                msg.setAsynchronous(true);
265dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                mHandler.sendMessageDelayed(msg, SETTLE_TIME_MILLIS);
2663b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            }
2673b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        }
2683b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    }
2693b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
270dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown    private void finishDetectionLocked() {
271dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown        if (mDetectionInProgress) {
272dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown            mSensorManager.unregisterListener(mListener);
273dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown            mHandler.removeCallbacks(mSensorTimeout);
274dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown
275dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown            if (mMustUpdateRestPosition) {
276dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                clearAtRestLocked();
277dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                if (mTotalSamples < MIN_SAMPLES) {
278dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                    Slog.w(TAG, "Wireless charger detector is broken.  Only received "
279dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                            + mTotalSamples + " samples from the gravity sensor but we "
280dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                            + "need at least " + MIN_SAMPLES + " and we expect to see "
281dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                            + "about " + SETTLE_TIME_MILLIS / SAMPLING_INTERVAL_MILLIS
282dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                            + " on average.");
283dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                } else if (mMovingSamples == 0) {
284dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                    mAtRest = true;
285dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                    mRestX = mLastSampleX;
286dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                    mRestY = mLastSampleY;
287dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                    mRestZ = mLastSampleZ;
288dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                }
289dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                mMustUpdateRestPosition = false;
2903b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            }
2913b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
292dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown            if (DEBUG) {
293dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                Slog.d(TAG, "New state: mAtRest=" + mAtRest
294dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                        + ", mRestX=" + mRestX + ", mRestY=" + mRestY + ", mRestZ=" + mRestZ
295dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                        + ", mTotalSamples=" + mTotalSamples
296dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                        + ", mMovingSamples=" + mMovingSamples);
297dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown            }
298dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown
299dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown            mDetectionInProgress = false;
300dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown            mSuspendBlocker.release();
301dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown        }
302dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown    }
303dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown
304dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown    private void processSampleLocked(float x, float y, float z) {
305dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown        if (mDetectionInProgress) {
30620e287534396655af0d5912d8b272070ad24a93aJeff Brown            mLastSampleX = x;
30720e287534396655af0d5912d8b272070ad24a93aJeff Brown            mLastSampleY = y;
30820e287534396655af0d5912d8b272070ad24a93aJeff Brown            mLastSampleZ = z;
30920e287534396655af0d5912d8b272070ad24a93aJeff Brown
3103b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            mTotalSamples += 1;
3113b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            if (mTotalSamples == 1) {
3123b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                // Save information about the first sample collected.
3133b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                mFirstSampleX = x;
3143b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                mFirstSampleY = y;
3153b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                mFirstSampleZ = z;
3163b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            } else {
3173b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                // Determine whether movement has occurred relative to the first sample.
3183b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                if (hasMoved(mFirstSampleX, mFirstSampleY, mFirstSampleZ, x, y, z)) {
3193b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                    mMovingSamples += 1;
3203b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                }
3213b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            }
3223b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
3233b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            // Clear the at rest flag if movement has occurred relative to the rest sample.
3243b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            if (mAtRest && hasMoved(mRestX, mRestY, mRestZ, x, y, z)) {
3253b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                if (DEBUG) {
3263b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                    Slog.d(TAG, "No longer at rest: "
3273b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                            + "mRestX=" + mRestX + ", mRestY=" + mRestY + ", mRestZ=" + mRestZ
3283b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                            + ", x=" + x + ", y=" + y + ", z=" + z);
3293b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                }
3303b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                clearAtRestLocked();
3313b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            }
3323b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        }
3333b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    }
3343b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
3353b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private void clearAtRestLocked() {
3363b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        mAtRest = false;
3373b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        mRestX = 0;
3383b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        mRestY = 0;
3393b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        mRestZ = 0;
3403b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    }
3413b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
3423b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private static boolean hasMoved(float x1, float y1, float z1,
3433b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            float x2, float y2, float z2) {
3443b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        final double dotProduct = (x1 * x2) + (y1 * y2) + (z1 * z2);
3453b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        final double mag1 = Math.sqrt((x1 * x1) + (y1 * y1) + (z1 * z1));
3463b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        final double mag2 = Math.sqrt((x2 * x2) + (y2 * y2) + (z2 * z2));
3473b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        if (mag1 < MIN_GRAVITY || mag1 > MAX_GRAVITY
3483b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                || mag2 < MIN_GRAVITY || mag2 > MAX_GRAVITY) {
3493b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            if (DEBUG) {
3503b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                Slog.d(TAG, "Weird gravity vector: mag1=" + mag1 + ", mag2=" + mag2);
3513b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            }
3523b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            return true;
3533b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        }
3543b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        final boolean moved = (dotProduct < mag1 * mag2 * MOVEMENT_ANGLE_COS_THRESHOLD);
3553b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        if (DEBUG) {
3563b971598ed28d45c176e3f9b076fc743a406296dJeff Brown            Slog.d(TAG, "Check: moved=" + moved
3573b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                    + ", x1=" + x1 + ", y1=" + y1 + ", z1=" + z1
3583b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                    + ", x2=" + x2 + ", y2=" + y2 + ", z2=" + z2
3593b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                    + ", angle=" + (Math.acos(dotProduct / mag1 / mag2) * 180 / Math.PI)
3603b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                    + ", dotProduct=" + dotProduct
3613b971598ed28d45c176e3f9b076fc743a406296dJeff Brown                    + ", mag1=" + mag1 + ", mag2=" + mag2);
3623b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        }
3633b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        return moved;
3643b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    }
3653b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
3663b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    private final SensorEventListener mListener = new SensorEventListener() {
3673b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        @Override
3683b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        public void onSensorChanged(SensorEvent event) {
369dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown            synchronized (mLock) {
370dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                processSampleLocked(event.values[0], event.values[1], event.values[2]);
371dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown            }
3723b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        }
3733b971598ed28d45c176e3f9b076fc743a406296dJeff Brown
3743b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        @Override
3753b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        public void onAccuracyChanged(Sensor sensor, int accuracy) {
3763b971598ed28d45c176e3f9b076fc743a406296dJeff Brown        }
3773b971598ed28d45c176e3f9b076fc743a406296dJeff Brown    };
378dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown
379dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown    private final Runnable mSensorTimeout = new Runnable() {
380dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown        @Override
381dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown        public void run() {
382dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown            synchronized (mLock) {
383dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown                finishDetectionLocked();
384dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown            }
385dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown        }
386dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown    };
3873b971598ed28d45c176e3f9b076fc743a406296dJeff Brown}
388