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; 27958d0a503699840f8f4b85cafa62f3793c89ac73Netta Pimport android.service.power.WirelessChargerDetectorProto; 283b971598ed28d45c176e3f9b076fc743a406296dJeff Brownimport android.util.Slog; 29dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brownimport android.util.TimeUtils; 30958d0a503699840f8f4b85cafa62f3793c89ac73Netta Pimport android.util.proto.ProtoOutputStream; 313b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 323b971598ed28d45c176e3f9b076fc743a406296dJeff Brownimport java.io.PrintWriter; 333b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 343b971598ed28d45c176e3f9b076fc743a406296dJeff Brown/** 353b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * Implements heuristics to detect docking or undocking from a wireless charger. 363b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * <p> 373b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * Some devices have wireless charging circuits that are unable to detect when the 383b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * device is resting on a wireless charger except when the device is actually 393b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * receiving power from the charger. The device may stop receiving power 403b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * if the battery is already nearly full or if it is too hot. As a result, we cannot 413b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * always rely on the battery service wireless plug signal to accurately indicate 423b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * whether the device has been docked or undocked from a wireless charger. 433b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p><p> 443b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * This is a problem because the power manager typically wakes up the screen and 453b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * plays a tone when the device is docked in a wireless charger. It is important 463b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * for the system to suppress spurious docking and undocking signals because they 473b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * can be intrusive for the user (especially if they cause a tone to be played 483b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * late at night for no apparent reason). 493b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p><p> 503b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * To avoid spurious signals, we apply some special policies to wireless chargers. 513b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p><p> 523b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * 1. Don't wake the device when undocked from the wireless charger because 533b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * it might be that the device is still resting on the wireless charger 543b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * but is not receiving power anymore because the battery is full. 553b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * Ideally we would wake the device if we could be certain that the user had 563b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * picked it up from the wireless charger but due to hardware limitations we 573b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * must be more conservative. 583b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p><p> 593b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * 2. Don't wake the device when docked on a wireless charger if the 603b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * battery already appears to be mostly full. This situation may indicate 613b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * that the device was resting on the charger the whole time and simply 623b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * wasn't receiving power because the battery was already full. We can't tell 633b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * whether the device was just placed on the charger or whether it has 643b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * been there for half of the night slowly discharging until it reached 653b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * the point where it needed to start charging again. So we suppress docking 663b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * signals that occur when the battery level is above a given threshold. 673b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p><p> 683b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * 3. Don't wake the device when docked on a wireless charger if it does 693b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * not appear to have moved since it was last undocked because it may 703b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * be that the prior undocking signal was spurious. We use the gravity 713b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * sensor to detect this case. 723b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * </p> 733b971598ed28d45c176e3f9b076fc743a406296dJeff Brown */ 743b971598ed28d45c176e3f9b076fc743a406296dJeff Brownfinal class WirelessChargerDetector { 753b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private static final String TAG = "WirelessChargerDetector"; 763b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private static final boolean DEBUG = false; 773b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 783b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The minimum amount of time to spend watching the sensor before making 793b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // a determination of whether movement occurred. 80dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown private static final long SETTLE_TIME_MILLIS = 800; 81dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown 82dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown // The sensor sampling interval. 83dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown private static final int SAMPLING_INTERVAL_MILLIS = 50; 843b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 853b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The minimum number of samples that must be collected. 863b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private static final int MIN_SAMPLES = 3; 873b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 883b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Upper bound on the battery charge percentage in order to consider turning 893b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // the screen on when the device starts charging wirelessly. 903b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private static final int WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT = 95; 913b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 923b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // To detect movement, we compute the angle between the gravity vector 933b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // at rest and the current gravity vector. This field specifies the 943b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // cosine of the maximum angle variance that we tolerate while at rest. 953b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private static final double MOVEMENT_ANGLE_COS_THRESHOLD = Math.cos(5 * Math.PI / 180); 963b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 973b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Sanity thresholds for the gravity vector. 983b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private static final double MIN_GRAVITY = SensorManager.GRAVITY_EARTH - 1.0f; 993b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private static final double MAX_GRAVITY = SensorManager.GRAVITY_EARTH + 1.0f; 1003b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1013b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private final Object mLock = new Object(); 1023b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1033b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private final SensorManager mSensorManager; 1043b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private final SuspendBlocker mSuspendBlocker; 105dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown private final Handler mHandler; 1063b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1073b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The gravity sensor, or null if none. 1083b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private Sensor mGravitySensor; 1093b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1103b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Previously observed wireless power state. 1113b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private boolean mPoweredWirelessly; 1123b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1133b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // True if the device is thought to be at rest on a wireless charger. 1143b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private boolean mAtRest; 1153b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1163b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The gravity vector most recently observed while at rest. 1173b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private float mRestX, mRestY, mRestZ; 1183b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1193b971598ed28d45c176e3f9b076fc743a406296dJeff Brown /* These properties are only meaningful while detection is in progress. */ 1203b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1213b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // True if detection is in progress. 1223b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The suspend blocker is held while this is the case. 1233b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private boolean mDetectionInProgress; 1243b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 125dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown // The time when detection was last performed. 126dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown private long mDetectionStartTime; 127dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown 1283b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // True if the rest position should be updated if at rest. 1293b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Otherwise, the current rest position is simply checked and cleared if movement 1303b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // is detected but no new rest position is stored. 1313b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private boolean mMustUpdateRestPosition; 1323b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1333b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The total number of samples collected. 1343b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private int mTotalSamples; 1353b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1363b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The number of samples collected that showed evidence of not being at rest. 1373b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private int mMovingSamples; 1383b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 139dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown // The value of the first sample that was collected. 1403b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private float mFirstSampleX, mFirstSampleY, mFirstSampleZ; 1413b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 142dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown // The value of the last sample that was collected. 14320e287534396655af0d5912d8b272070ad24a93aJeff Brown private float mLastSampleX, mLastSampleY, mLastSampleZ; 14420e287534396655af0d5912d8b272070ad24a93aJeff Brown 1453b971598ed28d45c176e3f9b076fc743a406296dJeff Brown public WirelessChargerDetector(SensorManager sensorManager, 146dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown SuspendBlocker suspendBlocker, Handler handler) { 1473b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mSensorManager = sensorManager; 1483b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mSuspendBlocker = suspendBlocker; 149dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mHandler = handler; 1503b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1513b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mGravitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY); 1523b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 1533b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 1543b971598ed28d45c176e3f9b076fc743a406296dJeff Brown public void dump(PrintWriter pw) { 1553b971598ed28d45c176e3f9b076fc743a406296dJeff Brown synchronized (mLock) { 1563b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(); 1573b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println("Wireless Charger Detector State:"); 1583b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mGravitySensor=" + mGravitySensor); 1593b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mPoweredWirelessly=" + mPoweredWirelessly); 1603b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mAtRest=" + mAtRest); 1613b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mRestX=" + mRestX + ", mRestY=" + mRestY + ", mRestZ=" + mRestZ); 1623b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mDetectionInProgress=" + mDetectionInProgress); 163dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown pw.println(" mDetectionStartTime=" + (mDetectionStartTime == 0 ? "0 (never)" 164dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown : TimeUtils.formatUptime(mDetectionStartTime))); 1653b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mMustUpdateRestPosition=" + mMustUpdateRestPosition); 1663b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mTotalSamples=" + mTotalSamples); 1673b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mMovingSamples=" + mMovingSamples); 1683b971598ed28d45c176e3f9b076fc743a406296dJeff Brown pw.println(" mFirstSampleX=" + mFirstSampleX 1693b971598ed28d45c176e3f9b076fc743a406296dJeff Brown + ", mFirstSampleY=" + mFirstSampleY + ", mFirstSampleZ=" + mFirstSampleZ); 17020e287534396655af0d5912d8b272070ad24a93aJeff Brown pw.println(" mLastSampleX=" + mLastSampleX 17120e287534396655af0d5912d8b272070ad24a93aJeff Brown + ", mLastSampleY=" + mLastSampleY + ", mLastSampleZ=" + mLastSampleZ); 1723b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 1733b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 1743b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 175958d0a503699840f8f4b85cafa62f3793c89ac73Netta P public void writeToProto(ProtoOutputStream proto, long fieldId) { 176958d0a503699840f8f4b85cafa62f3793c89ac73Netta P final long wcdToken = proto.start(fieldId); 177958d0a503699840f8f4b85cafa62f3793c89ac73Netta P synchronized (mLock) { 178958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.write(WirelessChargerDetectorProto.IS_POWERED_WIRELESSLY, mPoweredWirelessly); 179958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.write(WirelessChargerDetectorProto.IS_AT_REST, mAtRest); 180958d0a503699840f8f4b85cafa62f3793c89ac73Netta P 181958d0a503699840f8f4b85cafa62f3793c89ac73Netta P final long restVectorToken = proto.start(WirelessChargerDetectorProto.REST); 182958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.write(WirelessChargerDetectorProto.VectorProto.X, mRestX); 183958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.write(WirelessChargerDetectorProto.VectorProto.Y, mRestY); 184958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.write(WirelessChargerDetectorProto.VectorProto.Z, mRestZ); 185958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.end(restVectorToken); 186958d0a503699840f8f4b85cafa62f3793c89ac73Netta P 187958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.write( 188958d0a503699840f8f4b85cafa62f3793c89ac73Netta P WirelessChargerDetectorProto.IS_DETECTION_IN_PROGRESS, mDetectionInProgress); 189958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.write(WirelessChargerDetectorProto.DETECTION_START_TIME_MS, mDetectionStartTime); 190958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.write( 191958d0a503699840f8f4b85cafa62f3793c89ac73Netta P WirelessChargerDetectorProto.IS_MUST_UPDATE_REST_POSITION, 192958d0a503699840f8f4b85cafa62f3793c89ac73Netta P mMustUpdateRestPosition); 193958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.write(WirelessChargerDetectorProto.TOTAL_SAMPLES, mTotalSamples); 194958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.write(WirelessChargerDetectorProto.MOVING_SAMPLES, mMovingSamples); 195958d0a503699840f8f4b85cafa62f3793c89ac73Netta P 196958d0a503699840f8f4b85cafa62f3793c89ac73Netta P final long firstSampleVectorToken = 197958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.start(WirelessChargerDetectorProto.FIRST_SAMPLE); 198958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.write(WirelessChargerDetectorProto.VectorProto.X, mFirstSampleX); 199958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.write(WirelessChargerDetectorProto.VectorProto.Y, mFirstSampleY); 200958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.write(WirelessChargerDetectorProto.VectorProto.Z, mFirstSampleZ); 201958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.end(firstSampleVectorToken); 202958d0a503699840f8f4b85cafa62f3793c89ac73Netta P 203958d0a503699840f8f4b85cafa62f3793c89ac73Netta P final long lastSampleVectorToken = 204958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.start(WirelessChargerDetectorProto.LAST_SAMPLE); 205958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.write(WirelessChargerDetectorProto.VectorProto.X, mLastSampleX); 206958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.write(WirelessChargerDetectorProto.VectorProto.Y, mLastSampleY); 207958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.write(WirelessChargerDetectorProto.VectorProto.Z, mLastSampleZ); 208958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.end(lastSampleVectorToken); 209958d0a503699840f8f4b85cafa62f3793c89ac73Netta P } 210958d0a503699840f8f4b85cafa62f3793c89ac73Netta P proto.end(wcdToken); 211958d0a503699840f8f4b85cafa62f3793c89ac73Netta P } 212958d0a503699840f8f4b85cafa62f3793c89ac73Netta P 2133b971598ed28d45c176e3f9b076fc743a406296dJeff Brown /** 2143b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * Updates the charging state and returns true if docking was detected. 2153b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * 2163b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * @param isPowered True if the device is powered. 2173b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * @param plugType The current plug type. 2183b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * @param batteryLevel The current battery level. 2193b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * @return True if the device is determined to have just been docked on a wireless 2203b971598ed28d45c176e3f9b076fc743a406296dJeff Brown * charger, after suppressing spurious docking or undocking signals. 2213b971598ed28d45c176e3f9b076fc743a406296dJeff Brown */ 2223b971598ed28d45c176e3f9b076fc743a406296dJeff Brown public boolean update(boolean isPowered, int plugType, int batteryLevel) { 2233b971598ed28d45c176e3f9b076fc743a406296dJeff Brown synchronized (mLock) { 2243b971598ed28d45c176e3f9b076fc743a406296dJeff Brown final boolean wasPoweredWirelessly = mPoweredWirelessly; 2253b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 2263b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (isPowered && plugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) { 2273b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The device is receiving power from the wireless charger. 2283b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Update the rest position asynchronously. 2293b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mPoweredWirelessly = true; 2303b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mMustUpdateRestPosition = true; 2313b971598ed28d45c176e3f9b076fc743a406296dJeff Brown startDetectionLocked(); 2323b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } else { 2333b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The device may or may not be on the wireless charger depending on whether 2343b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // the unplug signal that we received was spurious. 2353b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mPoweredWirelessly = false; 2363b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (mAtRest) { 2373b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (plugType != 0 && plugType != BatteryManager.BATTERY_PLUGGED_WIRELESS) { 2383b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The device was plugged into a new non-wireless power source. 2393b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // It's safe to assume that it is no longer on the wireless charger. 2403b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mMustUpdateRestPosition = false; 2413b971598ed28d45c176e3f9b076fc743a406296dJeff Brown clearAtRestLocked(); 2423b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } else { 2433b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // The device may still be on the wireless charger but we don't know. 2443b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Check whether the device has remained at rest on the charger 2453b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // so that we will know to ignore the next wireless plug event 2463b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // if needed. 2473b971598ed28d45c176e3f9b076fc743a406296dJeff Brown startDetectionLocked(); 2483b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2493b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2503b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2513b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 2523b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Report that the device has been docked only if the device just started 2533b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // receiving power wirelessly, has a high enough battery level that we 2543b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // can be assured that charging was not delayed due to the battery previously 2553b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // having been full, and the device is not known to already be at rest 2563b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // on the wireless charger from earlier. 2573b971598ed28d45c176e3f9b076fc743a406296dJeff Brown return mPoweredWirelessly && !wasPoweredWirelessly 2583b971598ed28d45c176e3f9b076fc743a406296dJeff Brown && batteryLevel < WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT 2593b971598ed28d45c176e3f9b076fc743a406296dJeff Brown && !mAtRest; 2603b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2613b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2623b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 2633b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private void startDetectionLocked() { 2643b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (!mDetectionInProgress && mGravitySensor != null) { 2653b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (mSensorManager.registerListener(mListener, mGravitySensor, 266dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown SAMPLING_INTERVAL_MILLIS * 1000)) { 2673b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mSuspendBlocker.acquire(); 2683b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mDetectionInProgress = true; 269dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mDetectionStartTime = SystemClock.uptimeMillis(); 2703b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mTotalSamples = 0; 2713b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mMovingSamples = 0; 272dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown 273dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown Message msg = Message.obtain(mHandler, mSensorTimeout); 274dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown msg.setAsynchronous(true); 275dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mHandler.sendMessageDelayed(msg, SETTLE_TIME_MILLIS); 2763b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2773b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2783b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 2793b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 280dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown private void finishDetectionLocked() { 281dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown if (mDetectionInProgress) { 282dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mSensorManager.unregisterListener(mListener); 283dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mHandler.removeCallbacks(mSensorTimeout); 284dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown 285dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown if (mMustUpdateRestPosition) { 286dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown clearAtRestLocked(); 287dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown if (mTotalSamples < MIN_SAMPLES) { 288dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown Slog.w(TAG, "Wireless charger detector is broken. Only received " 289dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown + mTotalSamples + " samples from the gravity sensor but we " 290dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown + "need at least " + MIN_SAMPLES + " and we expect to see " 291dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown + "about " + SETTLE_TIME_MILLIS / SAMPLING_INTERVAL_MILLIS 292dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown + " on average."); 293dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown } else if (mMovingSamples == 0) { 294dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mAtRest = true; 295dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mRestX = mLastSampleX; 296dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mRestY = mLastSampleY; 297dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mRestZ = mLastSampleZ; 298dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown } 299dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mMustUpdateRestPosition = false; 3003b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3013b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 302dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown if (DEBUG) { 303dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown Slog.d(TAG, "New state: mAtRest=" + mAtRest 304dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown + ", mRestX=" + mRestX + ", mRestY=" + mRestY + ", mRestZ=" + mRestZ 305dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown + ", mTotalSamples=" + mTotalSamples 306dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown + ", mMovingSamples=" + mMovingSamples); 307dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown } 308dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown 309dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mDetectionInProgress = false; 310dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown mSuspendBlocker.release(); 311dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown } 312dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown } 313dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown 314dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown private void processSampleLocked(float x, float y, float z) { 315dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown if (mDetectionInProgress) { 31620e287534396655af0d5912d8b272070ad24a93aJeff Brown mLastSampleX = x; 31720e287534396655af0d5912d8b272070ad24a93aJeff Brown mLastSampleY = y; 31820e287534396655af0d5912d8b272070ad24a93aJeff Brown mLastSampleZ = z; 31920e287534396655af0d5912d8b272070ad24a93aJeff Brown 3203b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mTotalSamples += 1; 3213b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (mTotalSamples == 1) { 3223b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Save information about the first sample collected. 3233b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mFirstSampleX = x; 3243b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mFirstSampleY = y; 3253b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mFirstSampleZ = z; 3263b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } else { 3273b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Determine whether movement has occurred relative to the first sample. 3283b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (hasMoved(mFirstSampleX, mFirstSampleY, mFirstSampleZ, x, y, z)) { 3293b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mMovingSamples += 1; 3303b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3313b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3323b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 3333b971598ed28d45c176e3f9b076fc743a406296dJeff Brown // Clear the at rest flag if movement has occurred relative to the rest sample. 3343b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (mAtRest && hasMoved(mRestX, mRestY, mRestZ, x, y, z)) { 3353b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (DEBUG) { 3363b971598ed28d45c176e3f9b076fc743a406296dJeff Brown Slog.d(TAG, "No longer at rest: " 3373b971598ed28d45c176e3f9b076fc743a406296dJeff Brown + "mRestX=" + mRestX + ", mRestY=" + mRestY + ", mRestZ=" + mRestZ 3383b971598ed28d45c176e3f9b076fc743a406296dJeff Brown + ", x=" + x + ", y=" + y + ", z=" + z); 3393b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3403b971598ed28d45c176e3f9b076fc743a406296dJeff Brown clearAtRestLocked(); 3413b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3423b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3433b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3443b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 3453b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private void clearAtRestLocked() { 3463b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mAtRest = false; 3473b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mRestX = 0; 3483b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mRestY = 0; 3493b971598ed28d45c176e3f9b076fc743a406296dJeff Brown mRestZ = 0; 3503b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3513b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 3523b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private static boolean hasMoved(float x1, float y1, float z1, 3533b971598ed28d45c176e3f9b076fc743a406296dJeff Brown float x2, float y2, float z2) { 3543b971598ed28d45c176e3f9b076fc743a406296dJeff Brown final double dotProduct = (x1 * x2) + (y1 * y2) + (z1 * z2); 3553b971598ed28d45c176e3f9b076fc743a406296dJeff Brown final double mag1 = Math.sqrt((x1 * x1) + (y1 * y1) + (z1 * z1)); 3563b971598ed28d45c176e3f9b076fc743a406296dJeff Brown final double mag2 = Math.sqrt((x2 * x2) + (y2 * y2) + (z2 * z2)); 3573b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (mag1 < MIN_GRAVITY || mag1 > MAX_GRAVITY 3583b971598ed28d45c176e3f9b076fc743a406296dJeff Brown || mag2 < MIN_GRAVITY || mag2 > MAX_GRAVITY) { 3593b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (DEBUG) { 3603b971598ed28d45c176e3f9b076fc743a406296dJeff Brown Slog.d(TAG, "Weird gravity vector: mag1=" + mag1 + ", mag2=" + mag2); 3613b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3623b971598ed28d45c176e3f9b076fc743a406296dJeff Brown return true; 3633b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3643b971598ed28d45c176e3f9b076fc743a406296dJeff Brown final boolean moved = (dotProduct < mag1 * mag2 * MOVEMENT_ANGLE_COS_THRESHOLD); 3653b971598ed28d45c176e3f9b076fc743a406296dJeff Brown if (DEBUG) { 3663b971598ed28d45c176e3f9b076fc743a406296dJeff Brown Slog.d(TAG, "Check: moved=" + moved 3673b971598ed28d45c176e3f9b076fc743a406296dJeff Brown + ", x1=" + x1 + ", y1=" + y1 + ", z1=" + z1 3683b971598ed28d45c176e3f9b076fc743a406296dJeff Brown + ", x2=" + x2 + ", y2=" + y2 + ", z2=" + z2 3693b971598ed28d45c176e3f9b076fc743a406296dJeff Brown + ", angle=" + (Math.acos(dotProduct / mag1 / mag2) * 180 / Math.PI) 3703b971598ed28d45c176e3f9b076fc743a406296dJeff Brown + ", dotProduct=" + dotProduct 3713b971598ed28d45c176e3f9b076fc743a406296dJeff Brown + ", mag1=" + mag1 + ", mag2=" + mag2); 3723b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3733b971598ed28d45c176e3f9b076fc743a406296dJeff Brown return moved; 3743b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3753b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 3763b971598ed28d45c176e3f9b076fc743a406296dJeff Brown private final SensorEventListener mListener = new SensorEventListener() { 3773b971598ed28d45c176e3f9b076fc743a406296dJeff Brown @Override 3783b971598ed28d45c176e3f9b076fc743a406296dJeff Brown public void onSensorChanged(SensorEvent event) { 379dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown synchronized (mLock) { 380dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown processSampleLocked(event.values[0], event.values[1], event.values[2]); 381dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown } 3823b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3833b971598ed28d45c176e3f9b076fc743a406296dJeff Brown 3843b971598ed28d45c176e3f9b076fc743a406296dJeff Brown @Override 3853b971598ed28d45c176e3f9b076fc743a406296dJeff Brown public void onAccuracyChanged(Sensor sensor, int accuracy) { 3863b971598ed28d45c176e3f9b076fc743a406296dJeff Brown } 3873b971598ed28d45c176e3f9b076fc743a406296dJeff Brown }; 388dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown 389dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown private final Runnable mSensorTimeout = new Runnable() { 390dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown @Override 391dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown public void run() { 392dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown synchronized (mLock) { 393dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown finishDetectionLocked(); 394dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown } 395dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown } 396dbcc8a22de0827ecc546e48a1c5b96ecc0da73a0Jeff Brown }; 3973b971598ed28d45c176e3f9b076fc743a406296dJeff Brown} 398