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