blob: 7a60a5d1e11d2185c96cb842813ee8a3ac7663df [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
Sudheer Shankafc46e9b2016-10-21 17:55:27 -070019import android.app.ActivityManagerInternal;
Dianne Hackborn14272302014-06-10 23:13:02 -070020import android.database.ContentObserver;
Dianne Hackborn8c841092013-06-24 13:46:13 -070021import android.os.BatteryStats;
Jeff Brown21392762014-06-13 19:00:36 -070022
Sudheer Shanka292637f2017-09-25 10:36:23 -070023import android.os.PowerManager;
Dianne Hackborn2e441072015-10-28 18:00:57 -070024import android.os.ResultReceiver;
Dianne Hackborn354736e2016-08-22 17:00:05 -070025import android.os.ShellCallback;
Dianne Hackborn2e441072015-10-28 18:00:57 -070026import android.os.ShellCommand;
Yifan Hong98852792017-10-12 11:35:14 -070027import com.android.internal.annotations.VisibleForTesting;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import com.android.internal.app.IBatteryStats;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060029import com.android.internal.util.DumpUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import com.android.server.am.BatteryStatsService;
Adam Lesinskief2ea1f2013-12-05 16:48:06 -080031import com.android.server.lights.Light;
32import com.android.server.lights.LightsManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033
Sudheer Shankadc589ac2016-11-10 15:30:17 -080034import android.app.ActivityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.content.ContentResolver;
36import android.content.Context;
37import android.content.Intent;
38import android.content.pm.PackageManager;
Yifan Hong98852792017-10-12 11:35:14 -070039import android.hidl.manager.V1_0.IServiceManager;
40import android.hidl.manager.V1_0.IServiceNotification;
Yifan Hong932190b2017-10-11 11:00:51 -070041import android.hardware.health.V2_0.HealthInfo;
Yifan Hong89d55c12017-10-11 11:29:01 -070042import android.hardware.health.V2_0.IHealthInfoCallback;
Yifan Hong98852792017-10-12 11:35:14 -070043import android.hardware.health.V2_0.IHealth;
Yifan Hong1fd86f4c2017-10-09 16:50:33 -070044import android.hardware.health.V2_0.Result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.os.BatteryManager;
Kweku Adamse6b00c22017-10-23 16:46:45 -070046import android.os.BatteryManagerProto;
Jeff Brown21392762014-06-13 19:00:36 -070047import android.os.BatteryManagerInternal;
Yifan Hong1fd86f4c2017-10-09 16:50:33 -070048import android.os.BatteryProperty;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.os.Binder;
Dianne Hackborn8bdf5932010-10-15 12:54:40 -070050import android.os.FileUtils;
Jeff Brown605ea692012-10-05 16:33:10 -070051import android.os.Handler;
Yifan Hongcf9a8b22017-11-02 18:43:06 -070052import android.os.HandlerThread;
Todd Poynor26faecc2013-05-22 18:54:48 -070053import android.os.IBatteryPropertiesListener;
54import android.os.IBatteryPropertiesRegistrar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.os.IBinder;
Dan Egnor18e93962010-02-10 19:27:58 -080056import android.os.DropBoxManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.os.RemoteException;
58import android.os.ServiceManager;
59import android.os.SystemClock;
Yifan Hong8cc18ef2017-10-31 12:27:47 -070060import android.os.Trace;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061import android.os.UEventObserver;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070062import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.provider.Settings;
Netta Pe2a3cd82017-01-26 18:03:51 -080064import android.service.battery.BatteryServiceDumpProto;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065import android.util.EventLog;
Yifan Hong1fd86f4c2017-10-09 16:50:33 -070066import android.util.MutableInt;
Joe Onorato8a9b2202010-02-26 18:56:32 -080067import android.util.Slog;
Netta Pe2a3cd82017-01-26 18:03:51 -080068import android.util.proto.ProtoOutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069
70import java.io.File;
71import java.io.FileDescriptor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072import java.io.FileOutputStream;
73import java.io.IOException;
74import java.io.PrintWriter;
75
Yifan Hong98852792017-10-12 11:35:14 -070076import java.util.Arrays;
77import java.util.List;
78import java.util.NoSuchElementException;
Yifan Hongcf9a8b22017-11-02 18:43:06 -070079import java.util.Objects;
Yifan Hong89d55c12017-10-11 11:29:01 -070080import java.util.concurrent.atomic.AtomicReference;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
82/**
83 * <p>BatteryService monitors the charging status, and charge level of the device
84 * battery. When these values change this service broadcasts the new values
85 * to all {@link android.content.BroadcastReceiver IntentReceivers} that are
86 * watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED
87 * BATTERY_CHANGED} action.</p>
88 * <p>The new values are stored in the Intent data and can be retrieved by
89 * calling {@link android.content.Intent#getExtra Intent.getExtra} with the
90 * following keys:</p>
91 * <p>&quot;scale&quot; - int, the maximum value for the charge level</p>
92 * <p>&quot;level&quot; - int, charge level, from 0 through &quot;scale&quot; inclusive</p>
93 * <p>&quot;status&quot; - String, the current charging status.<br />
94 * <p>&quot;health&quot; - String, the current battery health.<br />
95 * <p>&quot;present&quot; - boolean, true if the battery is present<br />
96 * <p>&quot;icon-small&quot; - int, suggested small icon to use for this state</p>
97 * <p>&quot;plugged&quot; - int, 0 if the device is not plugged in; 1 if plugged
98 * into an AC power adapter; 2 if plugged in via USB.</p>
99 * <p>&quot;voltage&quot; - int, current battery voltage in millivolts</p>
100 * <p>&quot;temperature&quot; - int, current battery temperature in tenths of
101 * a degree Centigrade</p>
102 * <p>&quot;technology&quot; - String, the type of battery installed, e.g. "Li-ion"</p>
Jeff Brown605ea692012-10-05 16:33:10 -0700103 *
104 * <p>
105 * The battery service may be called by the power manager while holding its locks so
106 * we take care to post all outcalls into the activity manager to a handler.
107 *
108 * FIXME: Ideally the power manager would perform all of its calls into the battery
109 * service asynchronously itself.
110 * </p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 */
Jeff Brown21392762014-06-13 19:00:36 -0700112public final class BatteryService extends SystemService {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 private static final String TAG = BatteryService.class.getSimpleName();
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800114
Jeff Browna4d82042012-10-02 19:11:19 -0700115 private static final boolean DEBUG = false;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800116
Jeff Browna4d82042012-10-02 19:11:19 -0700117 private static final int BATTERY_SCALE = 100; // battery capacity is a percentage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118
Yifan Hong89d55c12017-10-11 11:29:01 -0700119 private static final long HEALTH_HAL_WAIT_MS = 1000;
120
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 // Used locally for determining when to make a last ditch effort to log
122 // discharge stats before the device dies.
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700123 private int mCriticalBatteryLevel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124
Jeff Sharkeyec43a6b2013-04-30 13:33:18 -0700125 private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" };
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800126
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 private static final String DUMPSYS_DATA_PATH = "/data/system/";
128
129 // This should probably be exposed in the API, though it's not critical
130 private static final int BATTERY_PLUGGED_NONE = 0;
131
132 private final Context mContext;
133 private final IBatteryStats mBatteryStats;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700134 BinderService mBinderService;
Jeff Brown605ea692012-10-05 16:33:10 -0700135 private final Handler mHandler;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800136
Jeff Browna4d82042012-10-02 19:11:19 -0700137 private final Object mLock = new Object();
138
Yifan Hong932190b2017-10-11 11:00:51 -0700139 private HealthInfo mHealthInfo;
140 private final HealthInfo mLastHealthInfo = new HealthInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 private boolean mBatteryLevelCritical;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142 private int mLastBatteryStatus;
143 private int mLastBatteryHealth;
144 private boolean mLastBatteryPresent;
145 private int mLastBatteryLevel;
146 private int mLastBatteryVoltage;
147 private int mLastBatteryTemperature;
148 private boolean mLastBatteryLevelCritical;
Adrian Roos76dc5a52015-07-21 16:20:36 -0700149 private int mLastMaxChargingCurrent;
Badhri Jagan Sridharanf92fcfe2015-10-27 13:59:34 -0700150 private int mLastMaxChargingVoltage;
Ruchi Kandoi6361e222016-04-07 11:28:30 -0700151 private int mLastChargeCounter;
Jeff Browna4d82042012-10-02 19:11:19 -0700152
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800153 private int mSequence = 1;
154
Jeff Browna4d82042012-10-02 19:11:19 -0700155 private int mInvalidCharger;
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700156 private int mLastInvalidCharger;
Mike Lockwoodd81b1f42009-09-25 09:32:19 -0400157
158 private int mLowBatteryWarningLevel;
159 private int mLowBatteryCloseWarningLevel;
Brian Muramatsuf3c74f32012-08-31 15:14:48 -0700160 private int mShutdownBatteryTemperature;
Mike Lockwoodd81b1f42009-09-25 09:32:19 -0400161
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 private int mPlugType;
163 private int mLastPlugType = -1; // Extra state so we can detect first run
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800164
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700165 private boolean mBatteryLevelLow;
166
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 private long mDischargeStartTime;
168 private int mDischargeStartLevel;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800169
Dianne Hackbornc428aae2012-10-03 16:38:22 -0700170 private boolean mUpdatesStopped;
171
Joe Onoratode1b3592010-10-25 20:36:47 -0700172 private Led mLed;
173
Dianne Hackborn8ec5b832009-07-01 21:19:35 -0700174 private boolean mSentLowBatteryBroadcast = false;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800175
Sudheer Shankafc46e9b2016-10-21 17:55:27 -0700176 private ActivityManagerInternal mActivityManagerInternal;
177
Yifan Hong89d55c12017-10-11 11:29:01 -0700178 private HealthServiceWrapper mHealthServiceWrapper;
179 private HealthHalCallback mHealthHalCallback;
Yifan Hong1fd86f4c2017-10-09 16:50:33 -0700180 private BatteryPropertiesRegistrar mBatteryPropertiesRegistrar;
Yifan Hongcf9a8b22017-11-02 18:43:06 -0700181 private HandlerThread mHandlerThread;
Yifan Hong89d55c12017-10-11 11:29:01 -0700182
Jeff Brown21392762014-06-13 19:00:36 -0700183 public BatteryService(Context context) {
184 super(context);
185
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 mContext = context;
Jeff Brown605ea692012-10-05 16:33:10 -0700187 mHandler = new Handler(true /*async*/);
Jeff Brown21392762014-06-13 19:00:36 -0700188 mLed = new Led(context, getLocalService(LightsManager.class));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 mBatteryStats = BatteryStatsService.getService();
Sudheer Shankafc46e9b2016-10-21 17:55:27 -0700190 mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700192 mCriticalBatteryLevel = mContext.getResources().getInteger(
193 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
Mike Lockwoodd81b1f42009-09-25 09:32:19 -0400194 mLowBatteryWarningLevel = mContext.getResources().getInteger(
195 com.android.internal.R.integer.config_lowBatteryWarningLevel);
Dianne Hackborn14272302014-06-10 23:13:02 -0700196 mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
197 com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
Brian Muramatsuf3c74f32012-08-31 15:14:48 -0700198 mShutdownBatteryTemperature = mContext.getResources().getInteger(
199 com.android.internal.R.integer.config_shutdownBatteryTemperature);
Mike Lockwoodd81b1f42009-09-25 09:32:19 -0400200
Mike Lockwooddeff9c82010-09-04 10:29:17 -0400201 // watch for invalid charger messages if the invalid_charger switch exists
202 if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
Dianne Hackborn2e441072015-10-28 18:00:57 -0700203 UEventObserver invalidChargerObserver = new UEventObserver() {
204 @Override
205 public void onUEvent(UEvent event) {
206 final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
207 synchronized (mLock) {
208 if (mInvalidCharger != invalidCharger) {
209 mInvalidCharger = invalidCharger;
210 }
211 }
212 }
213 };
214 invalidChargerObserver.startObserving(
Jeff Browna4d82042012-10-02 19:11:19 -0700215 "DEVPATH=/devices/virtual/switch/invalid_charger");
Mike Lockwooddeff9c82010-09-04 10:29:17 -0400216 }
Jeff Brown21392762014-06-13 19:00:36 -0700217 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218
Jeff Brown21392762014-06-13 19:00:36 -0700219 @Override
220 public void onStart() {
Yifan Hong89d55c12017-10-11 11:29:01 -0700221 registerHealthCallback();
Jeff Brown21392762014-06-13 19:00:36 -0700222
Dianne Hackborn2e441072015-10-28 18:00:57 -0700223 mBinderService = new BinderService();
224 publishBinderService("battery", mBinderService);
Yifan Hong1fd86f4c2017-10-09 16:50:33 -0700225 mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
226 publishBinderService("batteryproperties", mBatteryPropertiesRegistrar);
Jeff Brown21392762014-06-13 19:00:36 -0700227 publishLocalService(BatteryManagerInternal.class, new LocalService());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 }
229
Jeff Brown21392762014-06-13 19:00:36 -0700230 @Override
231 public void onBootPhase(int phase) {
232 if (phase == PHASE_ACTIVITY_MANAGER_READY) {
233 // check our power situation now that it is safe to display the shutdown dialog.
234 synchronized (mLock) {
235 ContentObserver obs = new ContentObserver(mHandler) {
236 @Override
237 public void onChange(boolean selfChange) {
238 synchronized (mLock) {
239 updateBatteryWarningLevelLocked();
240 }
Dianne Hackborn14272302014-06-10 23:13:02 -0700241 }
Jeff Brown21392762014-06-13 19:00:36 -0700242 };
243 final ContentResolver resolver = mContext.getContentResolver();
244 resolver.registerContentObserver(Settings.Global.getUriFor(
245 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
246 false, obs, UserHandle.USER_ALL);
247 updateBatteryWarningLevelLocked();
248 }
Jeff Browna4d82042012-10-02 19:11:19 -0700249 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250 }
251
Yifan Hong89d55c12017-10-11 11:29:01 -0700252 private void registerHealthCallback() {
Yifan Hong8cc18ef2017-10-31 12:27:47 -0700253 traceBegin("HealthInitWrapper");
Yifan Hong89d55c12017-10-11 11:29:01 -0700254 mHealthServiceWrapper = new HealthServiceWrapper();
255 mHealthHalCallback = new HealthHalCallback();
256 // IHealth is lazily retrieved.
257 try {
258 mHealthServiceWrapper.init(mHealthHalCallback,
259 new HealthServiceWrapper.IServiceManagerSupplier() {},
260 new HealthServiceWrapper.IHealthSupplier() {});
Yifan Hong78e8af12017-10-26 15:57:19 -0700261 } catch (RemoteException ex) {
262 Slog.e(TAG, "health: cannot register callback. (RemoteException)");
263 throw ex.rethrowFromSystemServer();
264 } catch (NoSuchElementException ex) {
265 Slog.e(TAG, "health: cannot register callback. (no supported health HAL service)");
266 throw ex;
Yifan Hong8cc18ef2017-10-31 12:27:47 -0700267 } finally {
268 traceEnd();
Yifan Hong89d55c12017-10-11 11:29:01 -0700269 }
270
Yifan Hong8cc18ef2017-10-31 12:27:47 -0700271 traceBegin("HealthInitWaitUpdate");
Yifan Hong89d55c12017-10-11 11:29:01 -0700272 // init register for new service notifications, and IServiceManager should return the
273 // existing service in a near future. Wait for this.update() to instantiate
274 // the initial mHealthInfo.
Yifan Hong78e8af12017-10-26 15:57:19 -0700275 long beforeWait = SystemClock.uptimeMillis();
Yifan Hong89d55c12017-10-11 11:29:01 -0700276 synchronized (mLock) {
Yifan Hong78e8af12017-10-26 15:57:19 -0700277 while (mHealthInfo == null) {
278 Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait) +
279 "ms for callbacks. Waiting another " + HEALTH_HAL_WAIT_MS + " ms...");
Yifan Hong89d55c12017-10-11 11:29:01 -0700280 try {
Yifan Hong78e8af12017-10-26 15:57:19 -0700281 mLock.wait(HEALTH_HAL_WAIT_MS);
Yifan Hong89d55c12017-10-11 11:29:01 -0700282 } catch (InterruptedException ex) {
Yifan Hong78e8af12017-10-26 15:57:19 -0700283 Slog.i(TAG, "health: InterruptedException when waiting for update. "
284 + " Continuing...");
Yifan Hong89d55c12017-10-11 11:29:01 -0700285 }
286 }
Yifan Hong89d55c12017-10-11 11:29:01 -0700287 }
288
Yifan Hong78e8af12017-10-26 15:57:19 -0700289 Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait)
290 + "ms and received the update.");
Yifan Hong8cc18ef2017-10-31 12:27:47 -0700291 traceEnd();
Yifan Hong89d55c12017-10-11 11:29:01 -0700292 }
293
Jeff Brown21392762014-06-13 19:00:36 -0700294 private void updateBatteryWarningLevelLocked() {
Dianne Hackborn14272302014-06-10 23:13:02 -0700295 final ContentResolver resolver = mContext.getContentResolver();
296 int defWarnLevel = mContext.getResources().getInteger(
297 com.android.internal.R.integer.config_lowBatteryWarningLevel);
298 mLowBatteryWarningLevel = Settings.Global.getInt(resolver,
299 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
300 if (mLowBatteryWarningLevel == 0) {
301 mLowBatteryWarningLevel = defWarnLevel;
302 }
303 if (mLowBatteryWarningLevel < mCriticalBatteryLevel) {
304 mLowBatteryWarningLevel = mCriticalBatteryLevel;
305 }
306 mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
307 com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
308 processValuesLocked(true);
309 }
310
Jeff Browna4d82042012-10-02 19:11:19 -0700311 private boolean isPoweredLocked(int plugTypeSet) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 // assume we are powered if battery state is unknown so
313 // the "stay on while plugged in" option will work.
Yifan Hong932190b2017-10-11 11:00:51 -0700314 if (mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 return true;
316 }
Yifan Hong932190b2017-10-11 11:00:51 -0700317 if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mHealthInfo.legacy.chargerAcOnline) {
Jeff Browna4d82042012-10-02 19:11:19 -0700318 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 }
Yifan Hong932190b2017-10-11 11:00:51 -0700320 if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mHealthInfo.legacy.chargerUsbOnline) {
Jeff Browna4d82042012-10-02 19:11:19 -0700321 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 }
Yifan Hong932190b2017-10-11 11:00:51 -0700323 if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mHealthInfo.legacy.chargerWirelessOnline) {
Jeff Browna4d82042012-10-02 19:11:19 -0700324 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325 }
Jeff Browna4d82042012-10-02 19:11:19 -0700326 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 }
328
Jeff Brown21392762014-06-13 19:00:36 -0700329 private boolean shouldSendBatteryLowLocked() {
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700330 final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
331 final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
332
333 /* The ACTION_BATTERY_LOW broadcast is sent in these situations:
334 * - is just un-plugged (previously was plugged) and battery level is
335 * less than or equal to WARNING, or
336 * - is not plugged and battery level falls to WARNING boundary
337 * (becomes <= mLowBatteryWarningLevel).
338 */
339 return !plugged
Yifan Hong932190b2017-10-11 11:00:51 -0700340 && mHealthInfo.legacy.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
341 && mHealthInfo.legacy.batteryLevel <= mLowBatteryWarningLevel
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700342 && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
343 }
344
Jeff Browna4d82042012-10-02 19:11:19 -0700345 private void shutdownIfNoPowerLocked() {
Mike Lockwood07a500f2009-08-12 09:56:44 -0400346 // shut down gracefully if our battery is critically low and we are not powered.
347 // wait until the system has booted before attempting to display the shutdown dialog.
Yifan Hong932190b2017-10-11 11:00:51 -0700348 if (mHealthInfo.legacy.batteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) {
Jeff Brown605ea692012-10-05 16:33:10 -0700349 mHandler.post(new Runnable() {
350 @Override
351 public void run() {
Sudheer Shankafc46e9b2016-10-21 17:55:27 -0700352 if (mActivityManagerInternal.isSystemReady()) {
Jeff Brown605ea692012-10-05 16:33:10 -0700353 Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
354 intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
Sudheer Shanka292637f2017-09-25 10:36:23 -0700355 intent.putExtra(Intent.EXTRA_REASON,
356 PowerManager.SHUTDOWN_LOW_BATTERY);
Jeff Brown605ea692012-10-05 16:33:10 -0700357 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
358 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
359 }
360 }
361 });
Mike Lockwood07a500f2009-08-12 09:56:44 -0400362 }
363 }
364
Jeff Browna4d82042012-10-02 19:11:19 -0700365 private void shutdownIfOverTempLocked() {
Brian Muramatsuf3c74f32012-08-31 15:14:48 -0700366 // shut down gracefully if temperature is too high (> 68.0C by default)
367 // wait until the system has booted before attempting to display the
368 // shutdown dialog.
Yifan Hong932190b2017-10-11 11:00:51 -0700369 if (mHealthInfo.legacy.batteryTemperature > mShutdownBatteryTemperature) {
Jeff Brown605ea692012-10-05 16:33:10 -0700370 mHandler.post(new Runnable() {
371 @Override
372 public void run() {
Sudheer Shankafc46e9b2016-10-21 17:55:27 -0700373 if (mActivityManagerInternal.isSystemReady()) {
Jeff Brown605ea692012-10-05 16:33:10 -0700374 Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
375 intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
Sudheer Shanka292637f2017-09-25 10:36:23 -0700376 intent.putExtra(Intent.EXTRA_REASON,
377 PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE);
Jeff Brown605ea692012-10-05 16:33:10 -0700378 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
379 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
380 }
381 }
382 });
Eric Olsen6a362a92010-03-26 15:38:41 -0700383 }
384 }
385
Yifan Hong89d55c12017-10-11 11:29:01 -0700386 private void update(HealthInfo info) {
Yifan Hong8cc18ef2017-10-31 12:27:47 -0700387 traceBegin("HealthInfoUpdate");
Todd Poynor26faecc2013-05-22 18:54:48 -0700388 synchronized (mLock) {
389 if (!mUpdatesStopped) {
Yifan Hong89d55c12017-10-11 11:29:01 -0700390 mHealthInfo = info;
Todd Poynor26faecc2013-05-22 18:54:48 -0700391 // Process the new values.
Dianne Hackborn14272302014-06-10 23:13:02 -0700392 processValuesLocked(false);
Yifan Hong89d55c12017-10-11 11:29:01 -0700393 mLock.notifyAll(); // for any waiters on new info
Dianne Hackborna1f1a3c2014-02-24 18:12:28 -0800394 } else {
Yifan Hong89d55c12017-10-11 11:29:01 -0700395 copy(mLastHealthInfo, info);
Todd Poynor26faecc2013-05-22 18:54:48 -0700396 }
Dianne Hackbornc428aae2012-10-03 16:38:22 -0700397 }
Yifan Hong8cc18ef2017-10-31 12:27:47 -0700398 traceEnd();
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700399 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400
Yifan Hong932190b2017-10-11 11:00:51 -0700401 private static void copy(HealthInfo dst, HealthInfo src) {
402 dst.legacy.chargerAcOnline = src.legacy.chargerAcOnline;
403 dst.legacy.chargerUsbOnline = src.legacy.chargerUsbOnline;
404 dst.legacy.chargerWirelessOnline = src.legacy.chargerWirelessOnline;
405 dst.legacy.maxChargingCurrent = src.legacy.maxChargingCurrent;
406 dst.legacy.maxChargingVoltage = src.legacy.maxChargingVoltage;
407 dst.legacy.batteryStatus = src.legacy.batteryStatus;
408 dst.legacy.batteryHealth = src.legacy.batteryHealth;
409 dst.legacy.batteryPresent = src.legacy.batteryPresent;
410 dst.legacy.batteryLevel = src.legacy.batteryLevel;
411 dst.legacy.batteryVoltage = src.legacy.batteryVoltage;
412 dst.legacy.batteryTemperature = src.legacy.batteryTemperature;
413 dst.legacy.batteryCurrent = src.legacy.batteryCurrent;
414 dst.legacy.batteryCycleCount = src.legacy.batteryCycleCount;
415 dst.legacy.batteryFullCharge = src.legacy.batteryFullCharge;
416 dst.legacy.batteryChargeCounter = src.legacy.batteryChargeCounter;
417 dst.legacy.batteryTechnology = src.legacy.batteryTechnology;
418 dst.batteryCurrentAverage = src.batteryCurrentAverage;
419 dst.batteryCapacity = src.batteryCapacity;
420 dst.energyCounter = src.energyCounter;
421 }
422
Dianne Hackborn14272302014-06-10 23:13:02 -0700423 private void processValuesLocked(boolean force) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700424 boolean logOutlier = false;
425 long dischargeDuration = 0;
Joe Onoratoa7e4cf9b2009-07-28 18:18:20 -0700426
Yifan Hong932190b2017-10-11 11:00:51 -0700427 mBatteryLevelCritical = (mHealthInfo.legacy.batteryLevel <= mCriticalBatteryLevel);
428 if (mHealthInfo.legacy.chargerAcOnline) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
Yifan Hong932190b2017-10-11 11:00:51 -0700430 } else if (mHealthInfo.legacy.chargerUsbOnline) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
Yifan Hong932190b2017-10-11 11:00:51 -0700432 } else if (mHealthInfo.legacy.chargerWirelessOnline) {
Brian Muramatsu37a37f42012-08-14 15:21:02 -0700433 mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 } else {
435 mPlugType = BATTERY_PLUGGED_NONE;
436 }
Brian Muramatsuf3c74f32012-08-31 15:14:48 -0700437
Jeff Browna4d82042012-10-02 19:11:19 -0700438 if (DEBUG) {
439 Slog.d(TAG, "Processing new values: "
Yifan Hong932190b2017-10-11 11:00:51 -0700440 + "info=" + mHealthInfo
Jeff Browna4d82042012-10-02 19:11:19 -0700441 + ", mBatteryLevelCritical=" + mBatteryLevelCritical
442 + ", mPlugType=" + mPlugType);
443 }
444
Dianne Hackborn6b7b4842010-06-14 17:17:44 -0700445 // Let the battery stats keep track of the current level.
446 try {
Yifan Hong932190b2017-10-11 11:00:51 -0700447 mBatteryStats.setBatteryState(mHealthInfo.legacy.batteryStatus, mHealthInfo.legacy.batteryHealth,
448 mPlugType, mHealthInfo.legacy.batteryLevel, mHealthInfo.legacy.batteryTemperature,
449 mHealthInfo.legacy.batteryVoltage, mHealthInfo.legacy.batteryChargeCounter,
450 mHealthInfo.legacy.batteryFullCharge);
Dianne Hackborn6b7b4842010-06-14 17:17:44 -0700451 } catch (RemoteException e) {
452 // Should never happen.
453 }
Brian Muramatsuf3c74f32012-08-31 15:14:48 -0700454
Jeff Browna4d82042012-10-02 19:11:19 -0700455 shutdownIfNoPowerLocked();
456 shutdownIfOverTempLocked();
Dianne Hackborn6b7b4842010-06-14 17:17:44 -0700457
Yifan Hong932190b2017-10-11 11:00:51 -0700458 if (force || (mHealthInfo.legacy.batteryStatus != mLastBatteryStatus ||
459 mHealthInfo.legacy.batteryHealth != mLastBatteryHealth ||
460 mHealthInfo.legacy.batteryPresent != mLastBatteryPresent ||
461 mHealthInfo.legacy.batteryLevel != mLastBatteryLevel ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 mPlugType != mLastPlugType ||
Yifan Hong932190b2017-10-11 11:00:51 -0700463 mHealthInfo.legacy.batteryVoltage != mLastBatteryVoltage ||
464 mHealthInfo.legacy.batteryTemperature != mLastBatteryTemperature ||
465 mHealthInfo.legacy.maxChargingCurrent != mLastMaxChargingCurrent ||
466 mHealthInfo.legacy.maxChargingVoltage != mLastMaxChargingVoltage ||
467 mHealthInfo.legacy.batteryChargeCounter != mLastChargeCounter ||
Dianne Hackborn14272302014-06-10 23:13:02 -0700468 mInvalidCharger != mLastInvalidCharger)) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800469
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 if (mPlugType != mLastPlugType) {
471 if (mLastPlugType == BATTERY_PLUGGED_NONE) {
472 // discharging -> charging
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800473
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 // There's no value in this data unless we've discharged at least once and the
475 // battery level has changed; so don't log until it does.
Yifan Hong932190b2017-10-11 11:00:51 -0700476 if (mDischargeStartTime != 0 && mDischargeStartLevel != mHealthInfo.legacy.batteryLevel) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700477 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
478 logOutlier = true;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800479 EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
Yifan Hong932190b2017-10-11 11:00:51 -0700480 mDischargeStartLevel, mHealthInfo.legacy.batteryLevel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481 // make sure we see a discharge event before logging again
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800482 mDischargeStartTime = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 }
484 } else if (mPlugType == BATTERY_PLUGGED_NONE) {
485 // charging -> discharging or we just powered up
486 mDischargeStartTime = SystemClock.elapsedRealtime();
Yifan Hong932190b2017-10-11 11:00:51 -0700487 mDischargeStartLevel = mHealthInfo.legacy.batteryLevel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 }
489 }
Yifan Hong932190b2017-10-11 11:00:51 -0700490 if (mHealthInfo.legacy.batteryStatus != mLastBatteryStatus ||
491 mHealthInfo.legacy.batteryHealth != mLastBatteryHealth ||
492 mHealthInfo.legacy.batteryPresent != mLastBatteryPresent ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800493 mPlugType != mLastPlugType) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800494 EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
Yifan Hong932190b2017-10-11 11:00:51 -0700495 mHealthInfo.legacy.batteryStatus, mHealthInfo.legacy.batteryHealth, mHealthInfo.legacy.batteryPresent ? 1 : 0,
496 mPlugType, mHealthInfo.legacy.batteryTechnology);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 }
Yifan Hong932190b2017-10-11 11:00:51 -0700498 if (mHealthInfo.legacy.batteryLevel != mLastBatteryLevel) {
Dianne Hackborncf1171642013-07-12 17:26:02 -0700499 // Don't do this just from voltage or temperature changes, that is
500 // too noisy.
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800501 EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
Yifan Hong932190b2017-10-11 11:00:51 -0700502 mHealthInfo.legacy.batteryLevel, mHealthInfo.legacy.batteryVoltage, mHealthInfo.legacy.batteryTemperature);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 }
504 if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
505 mPlugType == BATTERY_PLUGGED_NONE) {
506 // We want to make sure we log discharge cycle outliers
507 // if the battery is about to die.
The Android Open Source Project10592532009-03-18 17:39:46 -0700508 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
509 logOutlier = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800511
Dianne Hackborn14272302014-06-10 23:13:02 -0700512 if (!mBatteryLevelLow) {
513 // Should we now switch in to low battery mode?
514 if (mPlugType == BATTERY_PLUGGED_NONE
Yifan Hong932190b2017-10-11 11:00:51 -0700515 && mHealthInfo.legacy.batteryLevel <= mLowBatteryWarningLevel) {
Dianne Hackborn14272302014-06-10 23:13:02 -0700516 mBatteryLevelLow = true;
517 }
518 } else {
519 // Should we now switch out of low battery mode?
520 if (mPlugType != BATTERY_PLUGGED_NONE) {
521 mBatteryLevelLow = false;
Yifan Hong932190b2017-10-11 11:00:51 -0700522 } else if (mHealthInfo.legacy.batteryLevel >= mLowBatteryCloseWarningLevel) {
Dianne Hackborn14272302014-06-10 23:13:02 -0700523 mBatteryLevelLow = false;
Yifan Hong932190b2017-10-11 11:00:51 -0700524 } else if (force && mHealthInfo.legacy.batteryLevel >= mLowBatteryWarningLevel) {
Dianne Hackborn14272302014-06-10 23:13:02 -0700525 // If being forced, the previous state doesn't matter, we will just
526 // absolutely check to see if we are now above the warning level.
527 mBatteryLevelLow = false;
528 }
529 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800530
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800531 mSequence++;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800532
Christopher Tate06ba5542009-04-09 16:03:56 -0700533 // Separate broadcast is sent for power connected / not connected
534 // since the standard intent will not wake any applications and some
535 // applications may want to have smart behavior based on this.
536 if (mPlugType != 0 && mLastPlugType == 0) {
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800537 final Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);
538 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
539 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
Jeff Brown605ea692012-10-05 16:33:10 -0700540 mHandler.post(new Runnable() {
541 @Override
542 public void run() {
Jeff Brown605ea692012-10-05 16:33:10 -0700543 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
544 }
545 });
Christopher Tate06ba5542009-04-09 16:03:56 -0700546 }
547 else if (mPlugType == 0 && mLastPlugType != 0) {
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800548 final Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
549 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
550 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
Jeff Brown605ea692012-10-05 16:33:10 -0700551 mHandler.post(new Runnable() {
552 @Override
553 public void run() {
Jeff Brown605ea692012-10-05 16:33:10 -0700554 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
555 }
556 });
Christopher Tate06ba5542009-04-09 16:03:56 -0700557 }
Mihai Predaa82842f2009-04-29 15:05:56 +0200558
Dianne Hackborn14272302014-06-10 23:13:02 -0700559 if (shouldSendBatteryLowLocked()) {
Dianne Hackborn8ec5b832009-07-01 21:19:35 -0700560 mSentLowBatteryBroadcast = true;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800561 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);
562 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
563 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
Jeff Brown605ea692012-10-05 16:33:10 -0700564 mHandler.post(new Runnable() {
565 @Override
566 public void run() {
Jeff Brown605ea692012-10-05 16:33:10 -0700567 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
568 }
569 });
Dianne Hackborn532ea262017-03-17 17:50:55 -0700570 } else if (mSentLowBatteryBroadcast &&
Yifan Hong932190b2017-10-11 11:00:51 -0700571 mHealthInfo.legacy.batteryLevel >= mLowBatteryCloseWarningLevel) {
Dianne Hackborn8ec5b832009-07-01 21:19:35 -0700572 mSentLowBatteryBroadcast = false;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800573 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
574 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
575 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
Jeff Brown605ea692012-10-05 16:33:10 -0700576 mHandler.post(new Runnable() {
577 @Override
578 public void run() {
Jeff Brown605ea692012-10-05 16:33:10 -0700579 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
580 }
581 });
Mihai Predaa82842f2009-04-29 15:05:56 +0200582 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800583
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800584 // We are doing this after sending the above broadcasts, so anything processing
585 // them will get the new sequence number at that point. (See for example how testing
586 // of JobScheduler's BatteryController works.)
587 sendIntentLocked();
588
Joe Onoratode1b3592010-10-25 20:36:47 -0700589 // Update the battery LED
590 mLed.updateLightsLocked();
591
The Android Open Source Project10592532009-03-18 17:39:46 -0700592 // This needs to be done after sendIntent() so that we get the lastest battery stats.
593 if (logOutlier && dischargeDuration != 0) {
Jeff Browna4d82042012-10-02 19:11:19 -0700594 logOutlierLocked(dischargeDuration);
The Android Open Source Project10592532009-03-18 17:39:46 -0700595 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800596
Yifan Hong932190b2017-10-11 11:00:51 -0700597 mLastBatteryStatus = mHealthInfo.legacy.batteryStatus;
598 mLastBatteryHealth = mHealthInfo.legacy.batteryHealth;
599 mLastBatteryPresent = mHealthInfo.legacy.batteryPresent;
600 mLastBatteryLevel = mHealthInfo.legacy.batteryLevel;
Dianne Hackborn99f7eb452009-09-22 17:27:53 -0700601 mLastPlugType = mPlugType;
Yifan Hong932190b2017-10-11 11:00:51 -0700602 mLastBatteryVoltage = mHealthInfo.legacy.batteryVoltage;
603 mLastBatteryTemperature = mHealthInfo.legacy.batteryTemperature;
604 mLastMaxChargingCurrent = mHealthInfo.legacy.maxChargingCurrent;
605 mLastMaxChargingVoltage = mHealthInfo.legacy.maxChargingVoltage;
606 mLastChargeCounter = mHealthInfo.legacy.batteryChargeCounter;
Dianne Hackborn99f7eb452009-09-22 17:27:53 -0700607 mLastBatteryLevelCritical = mBatteryLevelCritical;
Mike Lockwooddeff9c82010-09-04 10:29:17 -0400608 mLastInvalidCharger = mInvalidCharger;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 }
610 }
611
Jeff Browna4d82042012-10-02 19:11:19 -0700612 private void sendIntentLocked() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613 // Pack up the values and broadcast them to everyone
Jeff Brown605ea692012-10-05 16:33:10 -0700614 final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800615 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
616 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800617
Yifan Hong932190b2017-10-11 11:00:51 -0700618 int icon = getIconLocked(mHealthInfo.legacy.batteryLevel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800619
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800620 intent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
Yifan Hong932190b2017-10-11 11:00:51 -0700621 intent.putExtra(BatteryManager.EXTRA_STATUS, mHealthInfo.legacy.batteryStatus);
622 intent.putExtra(BatteryManager.EXTRA_HEALTH, mHealthInfo.legacy.batteryHealth);
623 intent.putExtra(BatteryManager.EXTRA_PRESENT, mHealthInfo.legacy.batteryPresent);
624 intent.putExtra(BatteryManager.EXTRA_LEVEL, mHealthInfo.legacy.batteryLevel);
Dianne Hackbornedd93162009-09-19 14:03:05 -0700625 intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
626 intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
627 intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
Yifan Hong932190b2017-10-11 11:00:51 -0700628 intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.legacy.batteryVoltage);
629 intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.legacy.batteryTemperature);
630 intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mHealthInfo.legacy.batteryTechnology);
Mike Lockwooddeff9c82010-09-04 10:29:17 -0400631 intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
Yifan Hong932190b2017-10-11 11:00:51 -0700632 intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.legacy.maxChargingCurrent);
633 intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mHealthInfo.legacy.maxChargingVoltage);
634 intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.legacy.batteryChargeCounter);
Jeff Browna4d82042012-10-02 19:11:19 -0700635 if (DEBUG) {
Yifan Hong932190b2017-10-11 11:00:51 -0700636 Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. scale:" + BATTERY_SCALE
637 + ", info:" + mHealthInfo.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800638 }
639
Jeff Brown605ea692012-10-05 16:33:10 -0700640 mHandler.post(new Runnable() {
641 @Override
642 public void run() {
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800643 ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
Jeff Brown605ea692012-10-05 16:33:10 -0700644 }
645 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800646 }
647
Jeff Browna4d82042012-10-02 19:11:19 -0700648 private void logBatteryStatsLocked() {
Dianne Hackborn8c841092013-06-24 13:46:13 -0700649 IBinder batteryInfoService = ServiceManager.getService(BatteryStats.SERVICE_NAME);
Dan Egnor18e93962010-02-10 19:27:58 -0800650 if (batteryInfoService == null) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800651
Dan Egnor18e93962010-02-10 19:27:58 -0800652 DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
653 if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return;
654
655 File dumpFile = null;
656 FileOutputStream dumpStream = null;
657 try {
658 // dump the service to a file
Dianne Hackborn8c841092013-06-24 13:46:13 -0700659 dumpFile = new File(DUMPSYS_DATA_PATH + BatteryStats.SERVICE_NAME + ".dump");
Dan Egnor18e93962010-02-10 19:27:58 -0800660 dumpStream = new FileOutputStream(dumpFile);
661 batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);
Dianne Hackborn8bdf5932010-10-15 12:54:40 -0700662 FileUtils.sync(dumpStream);
Dan Egnor18e93962010-02-10 19:27:58 -0800663
664 // add dump file to drop box
665 db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT);
666 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800667 Slog.e(TAG, "failed to dump battery service", e);
Dan Egnor18e93962010-02-10 19:27:58 -0800668 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800669 Slog.e(TAG, "failed to write dumpsys file", e);
Dan Egnor18e93962010-02-10 19:27:58 -0800670 } finally {
671 // make sure we clean up
672 if (dumpStream != null) {
673 try {
674 dumpStream.close();
675 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800676 Slog.e(TAG, "failed to close dumpsys output stream");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677 }
Dan Egnor18e93962010-02-10 19:27:58 -0800678 }
679 if (dumpFile != null && !dumpFile.delete()) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800680 Slog.e(TAG, "failed to delete temporary dumpsys file: "
Dan Egnor18e93962010-02-10 19:27:58 -0800681 + dumpFile.getAbsolutePath());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 }
683 }
684 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800685
Jeff Browna4d82042012-10-02 19:11:19 -0700686 private void logOutlierLocked(long duration) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800687 ContentResolver cr = mContext.getContentResolver();
Jeff Sharkey625239a2012-09-26 22:03:49 -0700688 String dischargeThresholdString = Settings.Global.getString(cr,
689 Settings.Global.BATTERY_DISCHARGE_THRESHOLD);
690 String durationThresholdString = Settings.Global.getString(cr,
691 Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800692
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693 if (dischargeThresholdString != null && durationThresholdString != null) {
694 try {
695 long durationThreshold = Long.parseLong(durationThresholdString);
696 int dischargeThreshold = Integer.parseInt(dischargeThresholdString);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800697 if (duration <= durationThreshold &&
Yifan Hong932190b2017-10-11 11:00:51 -0700698 mDischargeStartLevel - mHealthInfo.legacy.batteryLevel >= dischargeThreshold) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 // If the discharge cycle is bad enough we want to know about it.
Jeff Browna4d82042012-10-02 19:11:19 -0700700 logBatteryStatsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701 }
Jeff Browna4d82042012-10-02 19:11:19 -0700702 if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703 " discharge threshold: " + dischargeThreshold);
Jeff Browna4d82042012-10-02 19:11:19 -0700704 if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +
Yifan Hong932190b2017-10-11 11:00:51 -0700705 (mDischargeStartLevel - mHealthInfo.legacy.batteryLevel));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800707 Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708 durationThresholdString + " or " + dischargeThresholdString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800709 }
710 }
711 }
712
Jeff Browna4d82042012-10-02 19:11:19 -0700713 private int getIconLocked(int level) {
Yifan Hong932190b2017-10-11 11:00:51 -0700714 if (mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715 return com.android.internal.R.drawable.stat_sys_battery_charge;
Yifan Hong932190b2017-10-11 11:00:51 -0700716 } else if (mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800717 return com.android.internal.R.drawable.stat_sys_battery;
Yifan Hong932190b2017-10-11 11:00:51 -0700718 } else if (mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
719 || mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
Jeff Browna4d82042012-10-02 19:11:19 -0700720 if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)
Yifan Hong932190b2017-10-11 11:00:51 -0700721 && mHealthInfo.legacy.batteryLevel >= 100) {
Joe Onorato794be402010-11-21 19:22:25 -0800722 return com.android.internal.R.drawable.stat_sys_battery_charge;
723 } else {
724 return com.android.internal.R.drawable.stat_sys_battery;
725 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726 } else {
727 return com.android.internal.R.drawable.stat_sys_battery_unknown;
728 }
729 }
730
Dianne Hackborn2e441072015-10-28 18:00:57 -0700731 class Shell extends ShellCommand {
732 @Override
733 public int onCommand(String cmd) {
734 return onShellCommand(this, cmd);
735 }
736
737 @Override
738 public void onHelp() {
739 PrintWriter pw = getOutPrintWriter();
740 dumpHelp(pw);
741 }
742 }
743
744 static void dumpHelp(PrintWriter pw) {
745 pw.println("Battery service (battery) commands:");
746 pw.println(" help");
747 pw.println(" Print this help text.");
Adam Lesinski29ddfe52017-03-29 19:29:00 -0700748 pw.println(" set [-f] [ac|usb|wireless|status|level|temp|present|invalid] <value>");
Dianne Hackborn2e441072015-10-28 18:00:57 -0700749 pw.println(" Force a battery property value, freezing battery state.");
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800750 pw.println(" -f: force a battery change broadcast be sent, prints new sequence.");
751 pw.println(" unplug [-f]");
Dianne Hackborn2e441072015-10-28 18:00:57 -0700752 pw.println(" Force battery unplugged, freezing battery state.");
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800753 pw.println(" -f: force a battery change broadcast be sent, prints new sequence.");
754 pw.println(" reset [-f]");
Dianne Hackborn2e441072015-10-28 18:00:57 -0700755 pw.println(" Unfreeze battery state, returning to current hardware values.");
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800756 pw.println(" -f: force a battery change broadcast be sent, prints new sequence.");
757 }
758
759 static final int OPTION_FORCE_UPDATE = 1<<0;
760
761 int parseOptions(Shell shell) {
762 String opt;
763 int opts = 0;
764 while ((opt = shell.getNextOption()) != null) {
765 if ("-f".equals(opt)) {
766 opts |= OPTION_FORCE_UPDATE;
767 }
768 }
769 return opts;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700770 }
771
772 int onShellCommand(Shell shell, String cmd) {
773 if (cmd == null) {
774 return shell.handleDefaultCommands(cmd);
775 }
776 PrintWriter pw = shell.getOutPrintWriter();
777 switch (cmd) {
778 case "unplug": {
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800779 int opts = parseOptions(shell);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700780 getContext().enforceCallingOrSelfPermission(
781 android.Manifest.permission.DEVICE_POWER, null);
782 if (!mUpdatesStopped) {
Yifan Hong932190b2017-10-11 11:00:51 -0700783 copy(mLastHealthInfo, mHealthInfo);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700784 }
Yifan Hong932190b2017-10-11 11:00:51 -0700785 mHealthInfo.legacy.chargerAcOnline = false;
786 mHealthInfo.legacy.chargerUsbOnline = false;
787 mHealthInfo.legacy.chargerWirelessOnline = false;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700788 long ident = Binder.clearCallingIdentity();
789 try {
790 mUpdatesStopped = true;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800791 processValuesFromShellLocked(pw, opts);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700792 } finally {
793 Binder.restoreCallingIdentity(ident);
794 }
795 } break;
796 case "set": {
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800797 int opts = parseOptions(shell);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700798 getContext().enforceCallingOrSelfPermission(
799 android.Manifest.permission.DEVICE_POWER, null);
800 final String key = shell.getNextArg();
801 if (key == null) {
802 pw.println("No property specified");
803 return -1;
804
805 }
806 final String value = shell.getNextArg();
807 if (value == null) {
808 pw.println("No value specified");
809 return -1;
810
811 }
812 try {
813 if (!mUpdatesStopped) {
Yifan Hong932190b2017-10-11 11:00:51 -0700814 copy(mLastHealthInfo, mHealthInfo);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700815 }
816 boolean update = true;
817 switch (key) {
Christopher Tate630d98b2017-03-07 14:12:26 -0800818 case "present":
Yifan Hong932190b2017-10-11 11:00:51 -0700819 mHealthInfo.legacy.batteryPresent = Integer.parseInt(value) != 0;
Christopher Tate630d98b2017-03-07 14:12:26 -0800820 break;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700821 case "ac":
Yifan Hong932190b2017-10-11 11:00:51 -0700822 mHealthInfo.legacy.chargerAcOnline = Integer.parseInt(value) != 0;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700823 break;
824 case "usb":
Yifan Hong932190b2017-10-11 11:00:51 -0700825 mHealthInfo.legacy.chargerUsbOnline = Integer.parseInt(value) != 0;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700826 break;
827 case "wireless":
Yifan Hong932190b2017-10-11 11:00:51 -0700828 mHealthInfo.legacy.chargerWirelessOnline = Integer.parseInt(value) != 0;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700829 break;
830 case "status":
Yifan Hong932190b2017-10-11 11:00:51 -0700831 mHealthInfo.legacy.batteryStatus = Integer.parseInt(value);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700832 break;
833 case "level":
Yifan Hong932190b2017-10-11 11:00:51 -0700834 mHealthInfo.legacy.batteryLevel = Integer.parseInt(value);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700835 break;
Adam Lesinski29ddfe52017-03-29 19:29:00 -0700836 case "temp":
Yifan Hong932190b2017-10-11 11:00:51 -0700837 mHealthInfo.legacy.batteryTemperature = Integer.parseInt(value);
Adam Lesinski29ddfe52017-03-29 19:29:00 -0700838 break;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700839 case "invalid":
840 mInvalidCharger = Integer.parseInt(value);
841 break;
842 default:
843 pw.println("Unknown set option: " + key);
844 update = false;
845 break;
846 }
847 if (update) {
848 long ident = Binder.clearCallingIdentity();
849 try {
850 mUpdatesStopped = true;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800851 processValuesFromShellLocked(pw, opts);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700852 } finally {
853 Binder.restoreCallingIdentity(ident);
854 }
855 }
856 } catch (NumberFormatException ex) {
857 pw.println("Bad value: " + value);
858 return -1;
859 }
860 } break;
861 case "reset": {
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800862 int opts = parseOptions(shell);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700863 getContext().enforceCallingOrSelfPermission(
864 android.Manifest.permission.DEVICE_POWER, null);
865 long ident = Binder.clearCallingIdentity();
866 try {
867 if (mUpdatesStopped) {
868 mUpdatesStopped = false;
Yifan Hong932190b2017-10-11 11:00:51 -0700869 copy(mHealthInfo, mLastHealthInfo);
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800870 processValuesFromShellLocked(pw, opts);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700871 }
872 } finally {
873 Binder.restoreCallingIdentity(ident);
874 }
875 } break;
876 default:
877 return shell.handleDefaultCommands(cmd);
878 }
879 return 0;
880 }
881
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800882 private void processValuesFromShellLocked(PrintWriter pw, int opts) {
883 processValuesLocked((opts & OPTION_FORCE_UPDATE) != 0);
884 if ((opts & OPTION_FORCE_UPDATE) != 0) {
885 pw.println(mSequence);
886 }
887 }
888
Dianne Hackborn2e441072015-10-28 18:00:57 -0700889 private void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Browna4d82042012-10-02 19:11:19 -0700890 synchronized (mLock) {
891 if (args == null || args.length == 0 || "-a".equals(args[0])) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700892 pw.println("Current Battery Service state:");
Dianne Hackbornc428aae2012-10-03 16:38:22 -0700893 if (mUpdatesStopped) {
894 pw.println(" (UPDATES STOPPED -- use 'reset' to restart)");
895 }
Yifan Hong932190b2017-10-11 11:00:51 -0700896 pw.println(" AC powered: " + mHealthInfo.legacy.chargerAcOnline);
897 pw.println(" USB powered: " + mHealthInfo.legacy.chargerUsbOnline);
898 pw.println(" Wireless powered: " + mHealthInfo.legacy.chargerWirelessOnline);
899 pw.println(" Max charging current: " + mHealthInfo.legacy.maxChargingCurrent);
900 pw.println(" Max charging voltage: " + mHealthInfo.legacy.maxChargingVoltage);
901 pw.println(" Charge counter: " + mHealthInfo.legacy.batteryChargeCounter);
902 pw.println(" status: " + mHealthInfo.legacy.batteryStatus);
903 pw.println(" health: " + mHealthInfo.legacy.batteryHealth);
904 pw.println(" present: " + mHealthInfo.legacy.batteryPresent);
905 pw.println(" level: " + mHealthInfo.legacy.batteryLevel);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700906 pw.println(" scale: " + BATTERY_SCALE);
Yifan Hong932190b2017-10-11 11:00:51 -0700907 pw.println(" voltage: " + mHealthInfo.legacy.batteryVoltage);
908 pw.println(" temperature: " + mHealthInfo.legacy.batteryTemperature);
909 pw.println(" technology: " + mHealthInfo.legacy.batteryTechnology);
Dianne Hackbornc428aae2012-10-03 16:38:22 -0700910 } else {
Dianne Hackborn2e441072015-10-28 18:00:57 -0700911 Shell shell = new Shell();
Dianne Hackborn354736e2016-08-22 17:00:05 -0700912 shell.exec(mBinderService, null, fd, null, args, null, new ResultReceiver(null));
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700913 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800914 }
915 }
Joe Onoratode1b3592010-10-25 20:36:47 -0700916
Netta Pe2a3cd82017-01-26 18:03:51 -0800917 private void dumpProto(FileDescriptor fd) {
918 final ProtoOutputStream proto = new ProtoOutputStream(fd);
919
920 synchronized (mLock) {
921 proto.write(BatteryServiceDumpProto.ARE_UPDATES_STOPPED, mUpdatesStopped);
Kweku Adamse6b00c22017-10-23 16:46:45 -0700922 int batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_NONE;
Yifan Hong932190b2017-10-11 11:00:51 -0700923 if (mHealthInfo.legacy.chargerAcOnline) {
Kweku Adamse6b00c22017-10-23 16:46:45 -0700924 batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_AC;
Yifan Hong932190b2017-10-11 11:00:51 -0700925 } else if (mHealthInfo.legacy.chargerUsbOnline) {
Kweku Adamse6b00c22017-10-23 16:46:45 -0700926 batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_USB;
Yifan Hong932190b2017-10-11 11:00:51 -0700927 } else if (mHealthInfo.legacy.chargerWirelessOnline) {
Kweku Adamse6b00c22017-10-23 16:46:45 -0700928 batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_WIRELESS;
Netta Pe2a3cd82017-01-26 18:03:51 -0800929 }
930 proto.write(BatteryServiceDumpProto.PLUGGED, batteryPluggedValue);
Yifan Hong932190b2017-10-11 11:00:51 -0700931 proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mHealthInfo.legacy.maxChargingCurrent);
932 proto.write(BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE, mHealthInfo.legacy.maxChargingVoltage);
933 proto.write(BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.legacy.batteryChargeCounter);
934 proto.write(BatteryServiceDumpProto.STATUS, mHealthInfo.legacy.batteryStatus);
935 proto.write(BatteryServiceDumpProto.HEALTH, mHealthInfo.legacy.batteryHealth);
936 proto.write(BatteryServiceDumpProto.IS_PRESENT, mHealthInfo.legacy.batteryPresent);
937 proto.write(BatteryServiceDumpProto.LEVEL, mHealthInfo.legacy.batteryLevel);
Netta Pe2a3cd82017-01-26 18:03:51 -0800938 proto.write(BatteryServiceDumpProto.SCALE, BATTERY_SCALE);
Yifan Hong932190b2017-10-11 11:00:51 -0700939 proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.legacy.batteryVoltage);
940 proto.write(BatteryServiceDumpProto.TEMPERATURE, mHealthInfo.legacy.batteryTemperature);
941 proto.write(BatteryServiceDumpProto.TECHNOLOGY, mHealthInfo.legacy.batteryTechnology);
Netta Pe2a3cd82017-01-26 18:03:51 -0800942 }
943 proto.flush();
944 }
945
Yifan Hong8cc18ef2017-10-31 12:27:47 -0700946 private static void traceBegin(String name) {
947 Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
948 }
949
950 private static void traceEnd() {
951 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
952 }
953
Jeff Browna4d82042012-10-02 19:11:19 -0700954 private final class Led {
Adam Lesinskief2ea1f2013-12-05 16:48:06 -0800955 private final Light mBatteryLight;
Joe Onoratode1b3592010-10-25 20:36:47 -0700956
Jeff Browna4d82042012-10-02 19:11:19 -0700957 private final int mBatteryLowARGB;
958 private final int mBatteryMediumARGB;
959 private final int mBatteryFullARGB;
960 private final int mBatteryLedOn;
961 private final int mBatteryLedOff;
962
Adam Lesinskief2ea1f2013-12-05 16:48:06 -0800963 public Led(Context context, LightsManager lights) {
964 mBatteryLight = lights.getLight(LightsManager.LIGHT_ID_BATTERY);
Joe Onoratode1b3592010-10-25 20:36:47 -0700965
Jeff Browna4d82042012-10-02 19:11:19 -0700966 mBatteryLowARGB = context.getResources().getInteger(
Joe Onoratode1b3592010-10-25 20:36:47 -0700967 com.android.internal.R.integer.config_notificationsBatteryLowARGB);
Jeff Browna4d82042012-10-02 19:11:19 -0700968 mBatteryMediumARGB = context.getResources().getInteger(
Joe Onoratode1b3592010-10-25 20:36:47 -0700969 com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
Jeff Browna4d82042012-10-02 19:11:19 -0700970 mBatteryFullARGB = context.getResources().getInteger(
Joe Onoratode1b3592010-10-25 20:36:47 -0700971 com.android.internal.R.integer.config_notificationsBatteryFullARGB);
Jeff Browna4d82042012-10-02 19:11:19 -0700972 mBatteryLedOn = context.getResources().getInteger(
Joe Onoratode1b3592010-10-25 20:36:47 -0700973 com.android.internal.R.integer.config_notificationsBatteryLedOn);
Jeff Browna4d82042012-10-02 19:11:19 -0700974 mBatteryLedOff = context.getResources().getInteger(
Joe Onoratode1b3592010-10-25 20:36:47 -0700975 com.android.internal.R.integer.config_notificationsBatteryLedOff);
976 }
977
978 /**
979 * Synchronize on BatteryService.
980 */
Jeff Browna4d82042012-10-02 19:11:19 -0700981 public void updateLightsLocked() {
Yifan Hong932190b2017-10-11 11:00:51 -0700982 final int level = mHealthInfo.legacy.batteryLevel;
983 final int status = mHealthInfo.legacy.batteryStatus;
Joe Onoratode1b3592010-10-25 20:36:47 -0700984 if (level < mLowBatteryWarningLevel) {
985 if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
986 // Solid red when battery is charging
987 mBatteryLight.setColor(mBatteryLowARGB);
988 } else {
989 // Flash red when battery is low and not charging
Adam Lesinskief2ea1f2013-12-05 16:48:06 -0800990 mBatteryLight.setFlashing(mBatteryLowARGB, Light.LIGHT_FLASH_TIMED,
Joe Onoratode1b3592010-10-25 20:36:47 -0700991 mBatteryLedOn, mBatteryLedOff);
992 }
993 } else if (status == BatteryManager.BATTERY_STATUS_CHARGING
994 || status == BatteryManager.BATTERY_STATUS_FULL) {
995 if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
996 // Solid green when full or charging and nearly full
997 mBatteryLight.setColor(mBatteryFullARGB);
998 } else {
999 // Solid orange when charging and halfway full
1000 mBatteryLight.setColor(mBatteryMediumARGB);
1001 }
1002 } else {
1003 // No lights if not charging and not low
1004 mBatteryLight.turnOff();
1005 }
1006 }
1007 }
Todd Poynor26faecc2013-05-22 18:54:48 -07001008
Yifan Hong89d55c12017-10-11 11:29:01 -07001009 private final class HealthHalCallback extends IHealthInfoCallback.Stub
1010 implements HealthServiceWrapper.Callback {
1011 @Override public void healthInfoChanged(HealthInfo props) {
1012 BatteryService.this.update(props);
1013 }
1014 // on new service registered
1015 @Override public void onRegistration(IHealth oldService, IHealth newService,
1016 String instance) {
1017 if (newService == null) return;
1018
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001019 traceBegin("HealthUnregisterCallback");
Adam Lesinskief2ea1f2013-12-05 16:48:06 -08001020 try {
Yifan Hong89d55c12017-10-11 11:29:01 -07001021 if (oldService != null) {
1022 int r = oldService.unregisterCallback(this);
1023 if (r != Result.SUCCESS) {
1024 Slog.w(TAG, "health: cannot unregister previous callback: " +
1025 Result.toString(r));
1026 }
1027 }
1028 } catch (RemoteException ex) {
1029 Slog.w(TAG, "health: cannot unregister previous callback (transaction error): "
1030 + ex.getMessage());
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001031 } finally {
1032 traceEnd();
Adam Lesinskief2ea1f2013-12-05 16:48:06 -08001033 }
Yifan Hong89d55c12017-10-11 11:29:01 -07001034
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001035 traceBegin("HealthRegisterCallback");
Yifan Hong89d55c12017-10-11 11:29:01 -07001036 try {
1037 int r = newService.registerCallback(this);
1038 if (r != Result.SUCCESS) {
1039 Slog.w(TAG, "health: cannot register callback: " + Result.toString(r));
1040 return;
1041 }
1042 // registerCallback does NOT guarantee that update is called
1043 // immediately, so request a manual update here.
1044 newService.update();
1045 } catch (RemoteException ex) {
1046 Slog.e(TAG, "health: cannot register callback (transaction error): "
1047 + ex.getMessage());
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001048 } finally {
1049 traceEnd();
Yifan Hong89d55c12017-10-11 11:29:01 -07001050 }
1051 }
Todd Poynor26faecc2013-05-22 18:54:48 -07001052 }
Jeff Brown21392762014-06-13 19:00:36 -07001053
1054 private final class BinderService extends Binder {
Dianne Hackborn2e441072015-10-28 18:00:57 -07001055 @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -06001056 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
Jeff Brown21392762014-06-13 19:00:36 -07001057
Netta Pe2a3cd82017-01-26 18:03:51 -08001058 if (args.length > 0 && "--proto".equals(args[0])) {
1059 dumpProto(fd);
1060 } else {
1061 dumpInternal(fd, pw, args);
1062 }
Dianne Hackborn2e441072015-10-28 18:00:57 -07001063 }
1064
1065 @Override public void onShellCommand(FileDescriptor in, FileDescriptor out,
Dianne Hackborn354736e2016-08-22 17:00:05 -07001066 FileDescriptor err, String[] args, ShellCallback callback,
1067 ResultReceiver resultReceiver) {
1068 (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
Jeff Brown21392762014-06-13 19:00:36 -07001069 }
1070 }
1071
Yifan Hong1fd86f4c2017-10-09 16:50:33 -07001072 // Reduced IBatteryPropertiesRegistrar that only implements getProperty for usage
1073 // in BatteryManager.
1074 private final class BatteryPropertiesRegistrar extends IBatteryPropertiesRegistrar.Stub {
1075 public void registerListener(IBatteryPropertiesListener listener) {
1076 Slog.e(TAG, "health: must not call registerListener on battery properties");
1077 }
1078 public void unregisterListener(IBatteryPropertiesListener listener) {
1079 Slog.e(TAG, "health: must not call unregisterListener on battery properties");
1080 }
1081 public int getProperty(int id, final BatteryProperty prop) throws RemoteException {
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001082 traceBegin("HealthGetProperty");
1083 try {
1084 IHealth service = mHealthServiceWrapper.getLastService();
1085 if (service == null) throw new RemoteException("no health service");
1086 final MutableInt outResult = new MutableInt(Result.NOT_SUPPORTED);
1087 switch(id) {
1088 case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER:
1089 service.getChargeCounter((int result, int value) -> {
1090 outResult.value = result;
1091 if (result == Result.SUCCESS) prop.setLong(value);
1092 });
1093 break;
1094 case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW:
1095 service.getCurrentNow((int result, int value) -> {
1096 outResult.value = result;
1097 if (result == Result.SUCCESS) prop.setLong(value);
1098 });
1099 break;
1100 case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE:
1101 service.getCurrentAverage((int result, int value) -> {
1102 outResult.value = result;
1103 if (result == Result.SUCCESS) prop.setLong(value);
1104 });
1105 break;
1106 case BatteryManager.BATTERY_PROPERTY_CAPACITY:
1107 service.getCapacity((int result, int value) -> {
1108 outResult.value = result;
1109 if (result == Result.SUCCESS) prop.setLong(value);
1110 });
1111 break;
1112 case BatteryManager.BATTERY_PROPERTY_STATUS:
1113 service.getChargeStatus((int result, int value) -> {
1114 outResult.value = result;
1115 if (result == Result.SUCCESS) prop.setLong(value);
1116 });
1117 break;
1118 case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER:
1119 service.getEnergyCounter((int result, long value) -> {
1120 outResult.value = result;
1121 if (result == Result.SUCCESS) prop.setLong(value);
1122 });
1123 break;
1124 }
1125 return outResult.value;
1126 } finally {
1127 traceEnd();
Yifan Hong1fd86f4c2017-10-09 16:50:33 -07001128 }
Yifan Hong1fd86f4c2017-10-09 16:50:33 -07001129 }
Yifan Hong7838fcb2017-10-24 16:06:27 -07001130 public void scheduleUpdate() throws RemoteException {
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001131 traceBegin("HealthScheduleUpdate");
1132 try {
1133 IHealth service = mHealthServiceWrapper.getLastService();
1134 if (service == null) throw new RemoteException("no health service");
1135 service.update();
1136 } finally {
1137 traceEnd();
1138 }
Yifan Hong1fd86f4c2017-10-09 16:50:33 -07001139 }
1140 }
1141
Jeff Brown21392762014-06-13 19:00:36 -07001142 private final class LocalService extends BatteryManagerInternal {
1143 @Override
1144 public boolean isPowered(int plugTypeSet) {
1145 synchronized (mLock) {
1146 return isPoweredLocked(plugTypeSet);
1147 }
1148 }
1149
1150 @Override
1151 public int getPlugType() {
1152 synchronized (mLock) {
1153 return mPlugType;
1154 }
1155 }
1156
1157 @Override
1158 public int getBatteryLevel() {
1159 synchronized (mLock) {
Yifan Hong932190b2017-10-11 11:00:51 -07001160 return mHealthInfo.legacy.batteryLevel;
Jeff Brown21392762014-06-13 19:00:36 -07001161 }
1162 }
1163
1164 @Override
1165 public boolean getBatteryLevelLow() {
1166 synchronized (mLock) {
1167 return mBatteryLevelLow;
1168 }
1169 }
1170
1171 @Override
1172 public int getInvalidCharger() {
1173 synchronized (mLock) {
1174 return mInvalidCharger;
1175 }
1176 }
1177 }
Yifan Hong98852792017-10-12 11:35:14 -07001178
1179 /**
1180 * HealthServiceWrapper wraps the internal IHealth service and refreshes the service when
1181 * necessary.
1182 *
1183 * On new registration of IHealth service, {@link #onRegistration onRegistration} is called and
1184 * the internal service is refreshed.
1185 * On death of an existing IHealth service, the internal service is NOT cleared to avoid
1186 * race condition between death notification and new service notification. Hence,
1187 * a caller must check for transaction errors when calling into the service.
1188 *
1189 * @hide Should only be used internally.
1190 */
1191 @VisibleForTesting
1192 static final class HealthServiceWrapper {
1193 private static final String TAG = "HealthServiceWrapper";
1194 public static final String INSTANCE_HEALTHD = "backup";
1195 public static final String INSTANCE_VENDOR = "default";
1196 // All interesting instances, sorted by priority high -> low.
1197 private static final List<String> sAllInstances =
1198 Arrays.asList(INSTANCE_VENDOR, INSTANCE_HEALTHD);
1199
1200 private final IServiceNotification mNotification = new Notification();
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001201 private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceRefresh");
Yifan Hong1fff9842017-10-27 13:53:10 -07001202 // These variables are fixed after init.
Yifan Hong98852792017-10-12 11:35:14 -07001203 private Callback mCallback;
1204 private IHealthSupplier mHealthSupplier;
Yifan Hong1fff9842017-10-27 13:53:10 -07001205 private String mInstanceName;
Yifan Hong98852792017-10-12 11:35:14 -07001206
Yifan Hong89d55c12017-10-11 11:29:01 -07001207 // Last IHealth service received.
Yifan Hong89d55c12017-10-11 11:29:01 -07001208 private final AtomicReference<IHealth> mLastService = new AtomicReference<>();
1209
Yifan Hong98852792017-10-12 11:35:14 -07001210 /**
1211 * init should be called after constructor. For testing purposes, init is not called by
1212 * constructor.
1213 */
1214 HealthServiceWrapper() {
1215 }
1216
Yifan Hong1fd86f4c2017-10-09 16:50:33 -07001217 IHealth getLastService() {
1218 return mLastService.get();
1219 }
1220
Yifan Hong98852792017-10-12 11:35:14 -07001221 /**
1222 * Start monitoring registration of new IHealth services. Only instances that are in
1223 * {@code sAllInstances} and in device / framework manifest are used. This function should
1224 * only be called once.
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001225 *
1226 * mCallback.onRegistration() is called synchronously (aka in init thread) before
1227 * this method returns.
1228 *
Yifan Hong98852792017-10-12 11:35:14 -07001229 * @throws RemoteException transaction error when talking to IServiceManager
1230 * @throws NoSuchElementException if one of the following cases:
1231 * - No service manager;
1232 * - none of {@code sAllInstances} are in manifests (i.e. not
1233 * available on this device), or none of these instances are available to current
1234 * process.
1235 * @throws NullPointerException when callback is null or supplier is null
1236 */
1237 void init(Callback callback,
1238 IServiceManagerSupplier managerSupplier,
1239 IHealthSupplier healthSupplier)
1240 throws RemoteException, NoSuchElementException, NullPointerException {
1241 if (callback == null || managerSupplier == null || healthSupplier == null)
1242 throw new NullPointerException();
1243
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001244 IServiceManager manager;
1245
Yifan Hong98852792017-10-12 11:35:14 -07001246 mCallback = callback;
1247 mHealthSupplier = healthSupplier;
1248
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001249 // Initialize mLastService and call callback for the first time (in init thread)
1250 IHealth newService = null;
Yifan Hong98852792017-10-12 11:35:14 -07001251 for (String name : sAllInstances) {
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001252 traceBegin("HealthInitGetService_" + name);
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001253 try {
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001254 newService = healthSupplier.get(name);
1255 } catch (NoSuchElementException ex) {
1256 /* ignored, handled below */
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001257 } finally {
1258 traceEnd();
Yifan Hong98852792017-10-12 11:35:14 -07001259 }
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001260 if (newService != null) {
1261 mInstanceName = name;
1262 mLastService.set(newService);
1263 break;
1264 }
Yifan Hong98852792017-10-12 11:35:14 -07001265 }
1266
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001267 if (mInstanceName == null || newService == null) {
Yifan Hong1fff9842017-10-27 13:53:10 -07001268 throw new NoSuchElementException(String.format(
1269 "No IHealth service instance among %s is available. Perhaps no permission?",
1270 sAllInstances.toString()));
1271 }
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001272 mCallback.onRegistration(null, newService, mInstanceName);
Yifan Hong1fff9842017-10-27 13:53:10 -07001273
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001274 // Register for future service registrations
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001275 traceBegin("HealthInitRegisterNotification");
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001276 mHandlerThread.start();
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001277 try {
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001278 managerSupplier.get().registerForNotifications(
1279 IHealth.kInterfaceName, mInstanceName, mNotification);
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001280 } finally {
1281 traceEnd();
1282 }
Yifan Hong1fff9842017-10-27 13:53:10 -07001283 Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + mInstanceName);
Yifan Hong98852792017-10-12 11:35:14 -07001284 }
1285
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001286 @VisibleForTesting
1287 HandlerThread getHandlerThread() {
1288 return mHandlerThread;
1289 }
1290
Yifan Hong98852792017-10-12 11:35:14 -07001291 interface Callback {
1292 /**
1293 * This function is invoked asynchronously when a new and related IServiceNotification
1294 * is received.
1295 * @param service the recently retrieved service from IServiceManager.
1296 * Can be a dead service before service notification of a new service is delivered.
1297 * Implementation must handle cases for {@link RemoteException}s when calling
1298 * into service.
1299 * @param instance instance name.
1300 */
Yifan Hong89d55c12017-10-11 11:29:01 -07001301 void onRegistration(IHealth oldService, IHealth newService, String instance);
Yifan Hong98852792017-10-12 11:35:14 -07001302 }
1303
1304 /**
1305 * Supplier of services.
1306 * Must not return null; throw {@link NoSuchElementException} if a service is not available.
1307 */
1308 interface IServiceManagerSupplier {
Yifan Hong89d55c12017-10-11 11:29:01 -07001309 default IServiceManager get() throws NoSuchElementException, RemoteException {
1310 return IServiceManager.getService();
1311 }
Yifan Hong98852792017-10-12 11:35:14 -07001312 }
1313 /**
1314 * Supplier of services.
1315 * Must not return null; throw {@link NoSuchElementException} if a service is not available.
1316 */
1317 interface IHealthSupplier {
Yifan Hong89d55c12017-10-11 11:29:01 -07001318 default IHealth get(String name) throws NoSuchElementException, RemoteException {
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001319 return IHealth.getService(name, true /* retry */);
Yifan Hong89d55c12017-10-11 11:29:01 -07001320 }
Yifan Hong98852792017-10-12 11:35:14 -07001321 }
1322
1323 private class Notification extends IServiceNotification.Stub {
1324 @Override
1325 public final void onRegistration(String interfaceName, String instanceName,
1326 boolean preexisting) {
1327 if (!IHealth.kInterfaceName.equals(interfaceName)) return;
Yifan Hong1fff9842017-10-27 13:53:10 -07001328 if (!mInstanceName.equals(instanceName)) return;
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001329
1330 // This runnable only runs on mHandlerThread and ordering is ensured, hence
1331 // no locking is needed inside the runnable.
1332 mHandlerThread.getThreadHandler().post(new Runnable() {
1333 @Override
1334 public void run() {
1335 try {
1336 IHealth newService = mHealthSupplier.get(mInstanceName);
1337 IHealth oldService = mLastService.getAndSet(newService);
1338
1339 // preexisting may be inaccurate (race). Check for equality here.
1340 if (Objects.equals(newService, oldService)) return;
1341
1342 Slog.i(TAG, "health: new instance registered " + mInstanceName);
1343 mCallback.onRegistration(oldService, newService, mInstanceName);
1344 } catch (NoSuchElementException | RemoteException ex) {
1345 Slog.e(TAG, "health: Cannot get instance '" + mInstanceName
1346 + "': " + ex.getMessage() + ". Perhaps no permission?");
1347 }
Yifan Hong89d55c12017-10-11 11:29:01 -07001348 }
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001349 });
Yifan Hong98852792017-10-12 11:35:14 -07001350 }
1351 }
1352 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001353}