blob: 7e66f35f05a1e510f02561ecb5340f808a951b07 [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 Honge41e9392017-11-06 12:56:59 -080041import android.hardware.health.V1_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;
Todd Poynor26faecc2013-05-22 18:54:48 -070052import android.os.IBatteryPropertiesListener;
53import android.os.IBatteryPropertiesRegistrar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.os.IBinder;
Dan Egnor18e93962010-02-10 19:27:58 -080055import android.os.DropBoxManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.os.RemoteException;
57import android.os.ServiceManager;
58import android.os.SystemClock;
59import android.os.UEventObserver;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070060import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061import android.provider.Settings;
Netta Pe2a3cd82017-01-26 18:03:51 -080062import android.service.battery.BatteryServiceDumpProto;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.util.EventLog;
Yifan Hong1fd86f4c2017-10-09 16:50:33 -070064import android.util.MutableInt;
Joe Onorato8a9b2202010-02-26 18:56:32 -080065import android.util.Slog;
Netta Pe2a3cd82017-01-26 18:03:51 -080066import android.util.proto.ProtoOutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067
68import java.io.File;
69import java.io.FileDescriptor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070import java.io.FileOutputStream;
71import java.io.IOException;
72import java.io.PrintWriter;
73
Yifan Hong98852792017-10-12 11:35:14 -070074import java.util.Arrays;
75import java.util.List;
76import java.util.NoSuchElementException;
Yifan Hong89d55c12017-10-11 11:29:01 -070077import java.util.concurrent.atomic.AtomicReference;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078
79/**
80 * <p>BatteryService monitors the charging status, and charge level of the device
81 * battery. When these values change this service broadcasts the new values
82 * to all {@link android.content.BroadcastReceiver IntentReceivers} that are
83 * watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED
84 * BATTERY_CHANGED} action.</p>
85 * <p>The new values are stored in the Intent data and can be retrieved by
86 * calling {@link android.content.Intent#getExtra Intent.getExtra} with the
87 * following keys:</p>
88 * <p>&quot;scale&quot; - int, the maximum value for the charge level</p>
89 * <p>&quot;level&quot; - int, charge level, from 0 through &quot;scale&quot; inclusive</p>
90 * <p>&quot;status&quot; - String, the current charging status.<br />
91 * <p>&quot;health&quot; - String, the current battery health.<br />
92 * <p>&quot;present&quot; - boolean, true if the battery is present<br />
93 * <p>&quot;icon-small&quot; - int, suggested small icon to use for this state</p>
94 * <p>&quot;plugged&quot; - int, 0 if the device is not plugged in; 1 if plugged
95 * into an AC power adapter; 2 if plugged in via USB.</p>
96 * <p>&quot;voltage&quot; - int, current battery voltage in millivolts</p>
97 * <p>&quot;temperature&quot; - int, current battery temperature in tenths of
98 * a degree Centigrade</p>
99 * <p>&quot;technology&quot; - String, the type of battery installed, e.g. "Li-ion"</p>
Jeff Brown605ea692012-10-05 16:33:10 -0700100 *
101 * <p>
102 * The battery service may be called by the power manager while holding its locks so
103 * we take care to post all outcalls into the activity manager to a handler.
104 *
105 * FIXME: Ideally the power manager would perform all of its calls into the battery
106 * service asynchronously itself.
107 * </p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 */
Jeff Brown21392762014-06-13 19:00:36 -0700109public final class BatteryService extends SystemService {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 private static final String TAG = BatteryService.class.getSimpleName();
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800111
Jeff Browna4d82042012-10-02 19:11:19 -0700112 private static final boolean DEBUG = false;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800113
Jeff Browna4d82042012-10-02 19:11:19 -0700114 private static final int BATTERY_SCALE = 100; // battery capacity is a percentage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115
Yifan Hong89d55c12017-10-11 11:29:01 -0700116 private static final long HEALTH_HAL_WAIT_MS = 1000;
117
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 // Used locally for determining when to make a last ditch effort to log
119 // discharge stats before the device dies.
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700120 private int mCriticalBatteryLevel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121
Jeff Sharkeyec43a6b2013-04-30 13:33:18 -0700122 private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" };
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800123
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 private static final String DUMPSYS_DATA_PATH = "/data/system/";
125
126 // This should probably be exposed in the API, though it's not critical
127 private static final int BATTERY_PLUGGED_NONE = 0;
128
129 private final Context mContext;
130 private final IBatteryStats mBatteryStats;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700131 BinderService mBinderService;
Jeff Brown605ea692012-10-05 16:33:10 -0700132 private final Handler mHandler;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800133
Jeff Browna4d82042012-10-02 19:11:19 -0700134 private final Object mLock = new Object();
135
Yifan Hong932190b2017-10-11 11:00:51 -0700136 private HealthInfo mHealthInfo;
137 private final HealthInfo mLastHealthInfo = new HealthInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 private boolean mBatteryLevelCritical;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 private int mLastBatteryStatus;
140 private int mLastBatteryHealth;
141 private boolean mLastBatteryPresent;
142 private int mLastBatteryLevel;
143 private int mLastBatteryVoltage;
144 private int mLastBatteryTemperature;
145 private boolean mLastBatteryLevelCritical;
Adrian Roos76dc5a52015-07-21 16:20:36 -0700146 private int mLastMaxChargingCurrent;
Badhri Jagan Sridharanf92fcfe2015-10-27 13:59:34 -0700147 private int mLastMaxChargingVoltage;
Ruchi Kandoi6361e222016-04-07 11:28:30 -0700148 private int mLastChargeCounter;
Jeff Browna4d82042012-10-02 19:11:19 -0700149
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800150 private int mSequence = 1;
151
Jeff Browna4d82042012-10-02 19:11:19 -0700152 private int mInvalidCharger;
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700153 private int mLastInvalidCharger;
Mike Lockwoodd81b1f42009-09-25 09:32:19 -0400154
155 private int mLowBatteryWarningLevel;
156 private int mLowBatteryCloseWarningLevel;
Brian Muramatsuf3c74f32012-08-31 15:14:48 -0700157 private int mShutdownBatteryTemperature;
Mike Lockwoodd81b1f42009-09-25 09:32:19 -0400158
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159 private int mPlugType;
160 private int mLastPlugType = -1; // Extra state so we can detect first run
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800161
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700162 private boolean mBatteryLevelLow;
163
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 private long mDischargeStartTime;
165 private int mDischargeStartLevel;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800166
Dianne Hackbornc428aae2012-10-03 16:38:22 -0700167 private boolean mUpdatesStopped;
168
Joe Onoratode1b3592010-10-25 20:36:47 -0700169 private Led mLed;
170
Dianne Hackborn8ec5b832009-07-01 21:19:35 -0700171 private boolean mSentLowBatteryBroadcast = false;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800172
Sudheer Shankafc46e9b2016-10-21 17:55:27 -0700173 private ActivityManagerInternal mActivityManagerInternal;
174
Yifan Hong89d55c12017-10-11 11:29:01 -0700175 private HealthServiceWrapper mHealthServiceWrapper;
176 private HealthHalCallback mHealthHalCallback;
Yifan Hong1fd86f4c2017-10-09 16:50:33 -0700177 private BatteryPropertiesRegistrar mBatteryPropertiesRegistrar;
Yifan Hong89d55c12017-10-11 11:29:01 -0700178
Jeff Brown21392762014-06-13 19:00:36 -0700179 public BatteryService(Context context) {
180 super(context);
181
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 mContext = context;
Jeff Brown605ea692012-10-05 16:33:10 -0700183 mHandler = new Handler(true /*async*/);
Jeff Brown21392762014-06-13 19:00:36 -0700184 mLed = new Led(context, getLocalService(LightsManager.class));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 mBatteryStats = BatteryStatsService.getService();
Sudheer Shankafc46e9b2016-10-21 17:55:27 -0700186 mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700188 mCriticalBatteryLevel = mContext.getResources().getInteger(
189 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
Mike Lockwoodd81b1f42009-09-25 09:32:19 -0400190 mLowBatteryWarningLevel = mContext.getResources().getInteger(
191 com.android.internal.R.integer.config_lowBatteryWarningLevel);
Dianne Hackborn14272302014-06-10 23:13:02 -0700192 mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
193 com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
Brian Muramatsuf3c74f32012-08-31 15:14:48 -0700194 mShutdownBatteryTemperature = mContext.getResources().getInteger(
195 com.android.internal.R.integer.config_shutdownBatteryTemperature);
Mike Lockwoodd81b1f42009-09-25 09:32:19 -0400196
Mike Lockwooddeff9c82010-09-04 10:29:17 -0400197 // watch for invalid charger messages if the invalid_charger switch exists
198 if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
Dianne Hackborn2e441072015-10-28 18:00:57 -0700199 UEventObserver invalidChargerObserver = new UEventObserver() {
200 @Override
201 public void onUEvent(UEvent event) {
202 final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
203 synchronized (mLock) {
204 if (mInvalidCharger != invalidCharger) {
205 mInvalidCharger = invalidCharger;
206 }
207 }
208 }
209 };
210 invalidChargerObserver.startObserving(
Jeff Browna4d82042012-10-02 19:11:19 -0700211 "DEVPATH=/devices/virtual/switch/invalid_charger");
Mike Lockwooddeff9c82010-09-04 10:29:17 -0400212 }
Jeff Brown21392762014-06-13 19:00:36 -0700213 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214
Jeff Brown21392762014-06-13 19:00:36 -0700215 @Override
216 public void onStart() {
Yifan Hong89d55c12017-10-11 11:29:01 -0700217 registerHealthCallback();
Jeff Brown21392762014-06-13 19:00:36 -0700218
Dianne Hackborn2e441072015-10-28 18:00:57 -0700219 mBinderService = new BinderService();
220 publishBinderService("battery", mBinderService);
Yifan Hong1fd86f4c2017-10-09 16:50:33 -0700221 mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
222 publishBinderService("batteryproperties", mBatteryPropertiesRegistrar);
Jeff Brown21392762014-06-13 19:00:36 -0700223 publishLocalService(BatteryManagerInternal.class, new LocalService());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 }
225
Jeff Brown21392762014-06-13 19:00:36 -0700226 @Override
227 public void onBootPhase(int phase) {
228 if (phase == PHASE_ACTIVITY_MANAGER_READY) {
229 // check our power situation now that it is safe to display the shutdown dialog.
230 synchronized (mLock) {
231 ContentObserver obs = new ContentObserver(mHandler) {
232 @Override
233 public void onChange(boolean selfChange) {
234 synchronized (mLock) {
235 updateBatteryWarningLevelLocked();
236 }
Dianne Hackborn14272302014-06-10 23:13:02 -0700237 }
Jeff Brown21392762014-06-13 19:00:36 -0700238 };
239 final ContentResolver resolver = mContext.getContentResolver();
240 resolver.registerContentObserver(Settings.Global.getUriFor(
241 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
242 false, obs, UserHandle.USER_ALL);
243 updateBatteryWarningLevelLocked();
244 }
Jeff Browna4d82042012-10-02 19:11:19 -0700245 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246 }
247
Yifan Hong89d55c12017-10-11 11:29:01 -0700248 private void registerHealthCallback() {
249 mHealthServiceWrapper = new HealthServiceWrapper();
250 mHealthHalCallback = new HealthHalCallback();
251 // IHealth is lazily retrieved.
252 try {
253 mHealthServiceWrapper.init(mHealthHalCallback,
254 new HealthServiceWrapper.IServiceManagerSupplier() {},
255 new HealthServiceWrapper.IHealthSupplier() {});
Yifan Hong78e8af12017-10-26 15:57:19 -0700256 } catch (RemoteException ex) {
257 Slog.e(TAG, "health: cannot register callback. (RemoteException)");
258 throw ex.rethrowFromSystemServer();
259 } catch (NoSuchElementException ex) {
260 Slog.e(TAG, "health: cannot register callback. (no supported health HAL service)");
261 throw ex;
Yifan Hong89d55c12017-10-11 11:29:01 -0700262 }
263
264 // init register for new service notifications, and IServiceManager should return the
265 // existing service in a near future. Wait for this.update() to instantiate
266 // the initial mHealthInfo.
Yifan Hong78e8af12017-10-26 15:57:19 -0700267 long beforeWait = SystemClock.uptimeMillis();
Yifan Hong89d55c12017-10-11 11:29:01 -0700268 synchronized (mLock) {
Yifan Hong78e8af12017-10-26 15:57:19 -0700269 while (mHealthInfo == null) {
270 Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait) +
271 "ms for callbacks. Waiting another " + HEALTH_HAL_WAIT_MS + " ms...");
Yifan Hong89d55c12017-10-11 11:29:01 -0700272 try {
Yifan Hong78e8af12017-10-26 15:57:19 -0700273 mLock.wait(HEALTH_HAL_WAIT_MS);
Yifan Hong89d55c12017-10-11 11:29:01 -0700274 } catch (InterruptedException ex) {
Yifan Hong78e8af12017-10-26 15:57:19 -0700275 Slog.i(TAG, "health: InterruptedException when waiting for update. "
276 + " Continuing...");
Yifan Hong89d55c12017-10-11 11:29:01 -0700277 }
278 }
Yifan Hong89d55c12017-10-11 11:29:01 -0700279 }
280
Yifan Hong78e8af12017-10-26 15:57:19 -0700281 Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait)
282 + "ms and received the update.");
Yifan Hong89d55c12017-10-11 11:29:01 -0700283 }
284
Jeff Brown21392762014-06-13 19:00:36 -0700285 private void updateBatteryWarningLevelLocked() {
Dianne Hackborn14272302014-06-10 23:13:02 -0700286 final ContentResolver resolver = mContext.getContentResolver();
287 int defWarnLevel = mContext.getResources().getInteger(
288 com.android.internal.R.integer.config_lowBatteryWarningLevel);
289 mLowBatteryWarningLevel = Settings.Global.getInt(resolver,
290 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
291 if (mLowBatteryWarningLevel == 0) {
292 mLowBatteryWarningLevel = defWarnLevel;
293 }
294 if (mLowBatteryWarningLevel < mCriticalBatteryLevel) {
295 mLowBatteryWarningLevel = mCriticalBatteryLevel;
296 }
297 mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
298 com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
299 processValuesLocked(true);
300 }
301
Jeff Browna4d82042012-10-02 19:11:19 -0700302 private boolean isPoweredLocked(int plugTypeSet) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 // assume we are powered if battery state is unknown so
304 // the "stay on while plugged in" option will work.
Yifan Honge41e9392017-11-06 12:56:59 -0800305 if (mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 return true;
307 }
Yifan Honge41e9392017-11-06 12:56:59 -0800308 if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mHealthInfo.chargerAcOnline) {
Jeff Browna4d82042012-10-02 19:11:19 -0700309 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 }
Yifan Honge41e9392017-11-06 12:56:59 -0800311 if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mHealthInfo.chargerUsbOnline) {
Jeff Browna4d82042012-10-02 19:11:19 -0700312 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 }
Yifan Honge41e9392017-11-06 12:56:59 -0800314 if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mHealthInfo.chargerWirelessOnline) {
Jeff Browna4d82042012-10-02 19:11:19 -0700315 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 }
Jeff Browna4d82042012-10-02 19:11:19 -0700317 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 }
319
Jeff Brown21392762014-06-13 19:00:36 -0700320 private boolean shouldSendBatteryLowLocked() {
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700321 final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
322 final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
323
324 /* The ACTION_BATTERY_LOW broadcast is sent in these situations:
325 * - is just un-plugged (previously was plugged) and battery level is
326 * less than or equal to WARNING, or
327 * - is not plugged and battery level falls to WARNING boundary
328 * (becomes <= mLowBatteryWarningLevel).
329 */
330 return !plugged
Yifan Honge41e9392017-11-06 12:56:59 -0800331 && mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
332 && mHealthInfo.batteryLevel <= mLowBatteryWarningLevel
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700333 && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
334 }
335
Jeff Browna4d82042012-10-02 19:11:19 -0700336 private void shutdownIfNoPowerLocked() {
Mike Lockwood07a500f2009-08-12 09:56:44 -0400337 // shut down gracefully if our battery is critically low and we are not powered.
338 // wait until the system has booted before attempting to display the shutdown dialog.
Yifan Honge41e9392017-11-06 12:56:59 -0800339 if (mHealthInfo.batteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) {
Jeff Brown605ea692012-10-05 16:33:10 -0700340 mHandler.post(new Runnable() {
341 @Override
342 public void run() {
Sudheer Shankafc46e9b2016-10-21 17:55:27 -0700343 if (mActivityManagerInternal.isSystemReady()) {
Jeff Brown605ea692012-10-05 16:33:10 -0700344 Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
345 intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
Sudheer Shanka292637f2017-09-25 10:36:23 -0700346 intent.putExtra(Intent.EXTRA_REASON,
347 PowerManager.SHUTDOWN_LOW_BATTERY);
Jeff Brown605ea692012-10-05 16:33:10 -0700348 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
349 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
350 }
351 }
352 });
Mike Lockwood07a500f2009-08-12 09:56:44 -0400353 }
354 }
355
Jeff Browna4d82042012-10-02 19:11:19 -0700356 private void shutdownIfOverTempLocked() {
Brian Muramatsuf3c74f32012-08-31 15:14:48 -0700357 // shut down gracefully if temperature is too high (> 68.0C by default)
358 // wait until the system has booted before attempting to display the
359 // shutdown dialog.
Yifan Honge41e9392017-11-06 12:56:59 -0800360 if (mHealthInfo.batteryTemperature > mShutdownBatteryTemperature) {
Jeff Brown605ea692012-10-05 16:33:10 -0700361 mHandler.post(new Runnable() {
362 @Override
363 public void run() {
Sudheer Shankafc46e9b2016-10-21 17:55:27 -0700364 if (mActivityManagerInternal.isSystemReady()) {
Jeff Brown605ea692012-10-05 16:33:10 -0700365 Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
366 intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
Sudheer Shanka292637f2017-09-25 10:36:23 -0700367 intent.putExtra(Intent.EXTRA_REASON,
368 PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE);
Jeff Brown605ea692012-10-05 16:33:10 -0700369 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
370 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
371 }
372 }
373 });
Eric Olsen6a362a92010-03-26 15:38:41 -0700374 }
375 }
376
Yifan Hong89d55c12017-10-11 11:29:01 -0700377 private void update(HealthInfo info) {
Todd Poynor26faecc2013-05-22 18:54:48 -0700378 synchronized (mLock) {
379 if (!mUpdatesStopped) {
Yifan Hong89d55c12017-10-11 11:29:01 -0700380 mHealthInfo = info;
Todd Poynor26faecc2013-05-22 18:54:48 -0700381 // Process the new values.
Dianne Hackborn14272302014-06-10 23:13:02 -0700382 processValuesLocked(false);
Yifan Hong89d55c12017-10-11 11:29:01 -0700383 mLock.notifyAll(); // for any waiters on new info
Dianne Hackborna1f1a3c2014-02-24 18:12:28 -0800384 } else {
Yifan Hong89d55c12017-10-11 11:29:01 -0700385 copy(mLastHealthInfo, info);
Todd Poynor26faecc2013-05-22 18:54:48 -0700386 }
Dianne Hackbornc428aae2012-10-03 16:38:22 -0700387 }
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700388 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389
Yifan Hong932190b2017-10-11 11:00:51 -0700390 private static void copy(HealthInfo dst, HealthInfo src) {
Yifan Honge41e9392017-11-06 12:56:59 -0800391 dst.chargerAcOnline = src.chargerAcOnline;
392 dst.chargerUsbOnline = src.chargerUsbOnline;
393 dst.chargerWirelessOnline = src.chargerWirelessOnline;
394 dst.maxChargingCurrent = src.maxChargingCurrent;
395 dst.maxChargingVoltage = src.maxChargingVoltage;
396 dst.batteryStatus = src.batteryStatus;
397 dst.batteryHealth = src.batteryHealth;
398 dst.batteryPresent = src.batteryPresent;
399 dst.batteryLevel = src.batteryLevel;
400 dst.batteryVoltage = src.batteryVoltage;
401 dst.batteryTemperature = src.batteryTemperature;
402 dst.batteryCurrent = src.batteryCurrent;
403 dst.batteryCycleCount = src.batteryCycleCount;
404 dst.batteryFullCharge = src.batteryFullCharge;
405 dst.batteryChargeCounter = src.batteryChargeCounter;
406 dst.batteryTechnology = src.batteryTechnology;
Yifan Hong932190b2017-10-11 11:00:51 -0700407 }
408
Dianne Hackborn14272302014-06-10 23:13:02 -0700409 private void processValuesLocked(boolean force) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700410 boolean logOutlier = false;
411 long dischargeDuration = 0;
Joe Onoratoa7e4cf9b2009-07-28 18:18:20 -0700412
Yifan Honge41e9392017-11-06 12:56:59 -0800413 mBatteryLevelCritical = (mHealthInfo.batteryLevel <= mCriticalBatteryLevel);
414 if (mHealthInfo.chargerAcOnline) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
Yifan Honge41e9392017-11-06 12:56:59 -0800416 } else if (mHealthInfo.chargerUsbOnline) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
Yifan Honge41e9392017-11-06 12:56:59 -0800418 } else if (mHealthInfo.chargerWirelessOnline) {
Brian Muramatsu37a37f42012-08-14 15:21:02 -0700419 mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 } else {
421 mPlugType = BATTERY_PLUGGED_NONE;
422 }
Brian Muramatsuf3c74f32012-08-31 15:14:48 -0700423
Jeff Browna4d82042012-10-02 19:11:19 -0700424 if (DEBUG) {
425 Slog.d(TAG, "Processing new values: "
Yifan Hong932190b2017-10-11 11:00:51 -0700426 + "info=" + mHealthInfo
Jeff Browna4d82042012-10-02 19:11:19 -0700427 + ", mBatteryLevelCritical=" + mBatteryLevelCritical
428 + ", mPlugType=" + mPlugType);
429 }
430
Dianne Hackborn6b7b4842010-06-14 17:17:44 -0700431 // Let the battery stats keep track of the current level.
432 try {
Yifan Honge41e9392017-11-06 12:56:59 -0800433 mBatteryStats.setBatteryState(mHealthInfo.batteryStatus, mHealthInfo.batteryHealth,
434 mPlugType, mHealthInfo.batteryLevel, mHealthInfo.batteryTemperature,
435 mHealthInfo.batteryVoltage, mHealthInfo.batteryChargeCounter,
436 mHealthInfo.batteryFullCharge);
Dianne Hackborn6b7b4842010-06-14 17:17:44 -0700437 } catch (RemoteException e) {
438 // Should never happen.
439 }
Brian Muramatsuf3c74f32012-08-31 15:14:48 -0700440
Jeff Browna4d82042012-10-02 19:11:19 -0700441 shutdownIfNoPowerLocked();
442 shutdownIfOverTempLocked();
Dianne Hackborn6b7b4842010-06-14 17:17:44 -0700443
Yifan Honge41e9392017-11-06 12:56:59 -0800444 if (force || (mHealthInfo.batteryStatus != mLastBatteryStatus ||
445 mHealthInfo.batteryHealth != mLastBatteryHealth ||
446 mHealthInfo.batteryPresent != mLastBatteryPresent ||
447 mHealthInfo.batteryLevel != mLastBatteryLevel ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 mPlugType != mLastPlugType ||
Yifan Honge41e9392017-11-06 12:56:59 -0800449 mHealthInfo.batteryVoltage != mLastBatteryVoltage ||
450 mHealthInfo.batteryTemperature != mLastBatteryTemperature ||
451 mHealthInfo.maxChargingCurrent != mLastMaxChargingCurrent ||
452 mHealthInfo.maxChargingVoltage != mLastMaxChargingVoltage ||
453 mHealthInfo.batteryChargeCounter != mLastChargeCounter ||
Dianne Hackborn14272302014-06-10 23:13:02 -0700454 mInvalidCharger != mLastInvalidCharger)) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800455
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 if (mPlugType != mLastPlugType) {
457 if (mLastPlugType == BATTERY_PLUGGED_NONE) {
458 // discharging -> charging
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800459
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 // There's no value in this data unless we've discharged at least once and the
461 // battery level has changed; so don't log until it does.
Yifan Honge41e9392017-11-06 12:56:59 -0800462 if (mDischargeStartTime != 0 && mDischargeStartLevel != mHealthInfo.batteryLevel) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700463 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
464 logOutlier = true;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800465 EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
Yifan Honge41e9392017-11-06 12:56:59 -0800466 mDischargeStartLevel, mHealthInfo.batteryLevel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 // make sure we see a discharge event before logging again
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800468 mDischargeStartTime = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 }
470 } else if (mPlugType == BATTERY_PLUGGED_NONE) {
471 // charging -> discharging or we just powered up
472 mDischargeStartTime = SystemClock.elapsedRealtime();
Yifan Honge41e9392017-11-06 12:56:59 -0800473 mDischargeStartLevel = mHealthInfo.batteryLevel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 }
475 }
Yifan Honge41e9392017-11-06 12:56:59 -0800476 if (mHealthInfo.batteryStatus != mLastBatteryStatus ||
477 mHealthInfo.batteryHealth != mLastBatteryHealth ||
478 mHealthInfo.batteryPresent != mLastBatteryPresent ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 mPlugType != mLastPlugType) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800480 EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
Yifan Honge41e9392017-11-06 12:56:59 -0800481 mHealthInfo.batteryStatus, mHealthInfo.batteryHealth, mHealthInfo.batteryPresent ? 1 : 0,
482 mPlugType, mHealthInfo.batteryTechnology);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 }
Yifan Honge41e9392017-11-06 12:56:59 -0800484 if (mHealthInfo.batteryLevel != mLastBatteryLevel) {
Dianne Hackborncf1171642013-07-12 17:26:02 -0700485 // Don't do this just from voltage or temperature changes, that is
486 // too noisy.
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800487 EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
Yifan Honge41e9392017-11-06 12:56:59 -0800488 mHealthInfo.batteryLevel, mHealthInfo.batteryVoltage, mHealthInfo.batteryTemperature);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 }
490 if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
491 mPlugType == BATTERY_PLUGGED_NONE) {
492 // We want to make sure we log discharge cycle outliers
493 // if the battery is about to die.
The Android Open Source Project10592532009-03-18 17:39:46 -0700494 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
495 logOutlier = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800497
Dianne Hackborn14272302014-06-10 23:13:02 -0700498 if (!mBatteryLevelLow) {
499 // Should we now switch in to low battery mode?
500 if (mPlugType == BATTERY_PLUGGED_NONE
Yifan Honge41e9392017-11-06 12:56:59 -0800501 && mHealthInfo.batteryLevel <= mLowBatteryWarningLevel) {
Dianne Hackborn14272302014-06-10 23:13:02 -0700502 mBatteryLevelLow = true;
503 }
504 } else {
505 // Should we now switch out of low battery mode?
506 if (mPlugType != BATTERY_PLUGGED_NONE) {
507 mBatteryLevelLow = false;
Yifan Honge41e9392017-11-06 12:56:59 -0800508 } else if (mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) {
Dianne Hackborn14272302014-06-10 23:13:02 -0700509 mBatteryLevelLow = false;
Yifan Honge41e9392017-11-06 12:56:59 -0800510 } else if (force && mHealthInfo.batteryLevel >= mLowBatteryWarningLevel) {
Dianne Hackborn14272302014-06-10 23:13:02 -0700511 // If being forced, the previous state doesn't matter, we will just
512 // absolutely check to see if we are now above the warning level.
513 mBatteryLevelLow = false;
514 }
515 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800516
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800517 mSequence++;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800518
Christopher Tate06ba5542009-04-09 16:03:56 -0700519 // Separate broadcast is sent for power connected / not connected
520 // since the standard intent will not wake any applications and some
521 // applications may want to have smart behavior based on this.
522 if (mPlugType != 0 && mLastPlugType == 0) {
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800523 final Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);
524 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
525 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
Jeff Brown605ea692012-10-05 16:33:10 -0700526 mHandler.post(new Runnable() {
527 @Override
528 public void run() {
Jeff Brown605ea692012-10-05 16:33:10 -0700529 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
530 }
531 });
Christopher Tate06ba5542009-04-09 16:03:56 -0700532 }
533 else if (mPlugType == 0 && mLastPlugType != 0) {
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800534 final Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
535 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
536 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
Jeff Brown605ea692012-10-05 16:33:10 -0700537 mHandler.post(new Runnable() {
538 @Override
539 public void run() {
Jeff Brown605ea692012-10-05 16:33:10 -0700540 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
541 }
542 });
Christopher Tate06ba5542009-04-09 16:03:56 -0700543 }
Mihai Predaa82842f2009-04-29 15:05:56 +0200544
Dianne Hackborn14272302014-06-10 23:13:02 -0700545 if (shouldSendBatteryLowLocked()) {
Dianne Hackborn8ec5b832009-07-01 21:19:35 -0700546 mSentLowBatteryBroadcast = true;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800547 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);
548 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
549 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
Jeff Brown605ea692012-10-05 16:33:10 -0700550 mHandler.post(new Runnable() {
551 @Override
552 public void run() {
Jeff Brown605ea692012-10-05 16:33:10 -0700553 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
554 }
555 });
Dianne Hackborn532ea262017-03-17 17:50:55 -0700556 } else if (mSentLowBatteryBroadcast &&
Yifan Honge41e9392017-11-06 12:56:59 -0800557 mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) {
Dianne Hackborn8ec5b832009-07-01 21:19:35 -0700558 mSentLowBatteryBroadcast = false;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800559 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
560 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
561 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
Jeff Brown605ea692012-10-05 16:33:10 -0700562 mHandler.post(new Runnable() {
563 @Override
564 public void run() {
Jeff Brown605ea692012-10-05 16:33:10 -0700565 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
566 }
567 });
Mihai Predaa82842f2009-04-29 15:05:56 +0200568 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800569
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800570 // We are doing this after sending the above broadcasts, so anything processing
571 // them will get the new sequence number at that point. (See for example how testing
572 // of JobScheduler's BatteryController works.)
573 sendIntentLocked();
574
Joe Onoratode1b3592010-10-25 20:36:47 -0700575 // Update the battery LED
576 mLed.updateLightsLocked();
577
The Android Open Source Project10592532009-03-18 17:39:46 -0700578 // This needs to be done after sendIntent() so that we get the lastest battery stats.
579 if (logOutlier && dischargeDuration != 0) {
Jeff Browna4d82042012-10-02 19:11:19 -0700580 logOutlierLocked(dischargeDuration);
The Android Open Source Project10592532009-03-18 17:39:46 -0700581 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800582
Yifan Honge41e9392017-11-06 12:56:59 -0800583 mLastBatteryStatus = mHealthInfo.batteryStatus;
584 mLastBatteryHealth = mHealthInfo.batteryHealth;
585 mLastBatteryPresent = mHealthInfo.batteryPresent;
586 mLastBatteryLevel = mHealthInfo.batteryLevel;
Dianne Hackborn99f7eb452009-09-22 17:27:53 -0700587 mLastPlugType = mPlugType;
Yifan Honge41e9392017-11-06 12:56:59 -0800588 mLastBatteryVoltage = mHealthInfo.batteryVoltage;
589 mLastBatteryTemperature = mHealthInfo.batteryTemperature;
590 mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrent;
591 mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltage;
592 mLastChargeCounter = mHealthInfo.batteryChargeCounter;
Dianne Hackborn99f7eb452009-09-22 17:27:53 -0700593 mLastBatteryLevelCritical = mBatteryLevelCritical;
Mike Lockwooddeff9c82010-09-04 10:29:17 -0400594 mLastInvalidCharger = mInvalidCharger;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 }
596 }
597
Jeff Browna4d82042012-10-02 19:11:19 -0700598 private void sendIntentLocked() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 // Pack up the values and broadcast them to everyone
Jeff Brown605ea692012-10-05 16:33:10 -0700600 final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800601 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
602 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800603
Yifan Honge41e9392017-11-06 12:56:59 -0800604 int icon = getIconLocked(mHealthInfo.batteryLevel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800605
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800606 intent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
Yifan Honge41e9392017-11-06 12:56:59 -0800607 intent.putExtra(BatteryManager.EXTRA_STATUS, mHealthInfo.batteryStatus);
608 intent.putExtra(BatteryManager.EXTRA_HEALTH, mHealthInfo.batteryHealth);
609 intent.putExtra(BatteryManager.EXTRA_PRESENT, mHealthInfo.batteryPresent);
610 intent.putExtra(BatteryManager.EXTRA_LEVEL, mHealthInfo.batteryLevel);
Dianne Hackbornedd93162009-09-19 14:03:05 -0700611 intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
612 intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
613 intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
Yifan Honge41e9392017-11-06 12:56:59 -0800614 intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage);
615 intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperature);
616 intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mHealthInfo.batteryTechnology);
Mike Lockwooddeff9c82010-09-04 10:29:17 -0400617 intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
Yifan Honge41e9392017-11-06 12:56:59 -0800618 intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent);
619 intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltage);
620 intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
Jeff Browna4d82042012-10-02 19:11:19 -0700621 if (DEBUG) {
Yifan Hong932190b2017-10-11 11:00:51 -0700622 Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. scale:" + BATTERY_SCALE
623 + ", info:" + mHealthInfo.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 }
625
Jeff Brown605ea692012-10-05 16:33:10 -0700626 mHandler.post(new Runnable() {
627 @Override
628 public void run() {
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800629 ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
Jeff Brown605ea692012-10-05 16:33:10 -0700630 }
631 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800632 }
633
Jeff Browna4d82042012-10-02 19:11:19 -0700634 private void logBatteryStatsLocked() {
Dianne Hackborn8c841092013-06-24 13:46:13 -0700635 IBinder batteryInfoService = ServiceManager.getService(BatteryStats.SERVICE_NAME);
Dan Egnor18e93962010-02-10 19:27:58 -0800636 if (batteryInfoService == null) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637
Dan Egnor18e93962010-02-10 19:27:58 -0800638 DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
639 if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return;
640
641 File dumpFile = null;
642 FileOutputStream dumpStream = null;
643 try {
644 // dump the service to a file
Dianne Hackborn8c841092013-06-24 13:46:13 -0700645 dumpFile = new File(DUMPSYS_DATA_PATH + BatteryStats.SERVICE_NAME + ".dump");
Dan Egnor18e93962010-02-10 19:27:58 -0800646 dumpStream = new FileOutputStream(dumpFile);
647 batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);
Dianne Hackborn8bdf5932010-10-15 12:54:40 -0700648 FileUtils.sync(dumpStream);
Dan Egnor18e93962010-02-10 19:27:58 -0800649
650 // add dump file to drop box
651 db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT);
652 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800653 Slog.e(TAG, "failed to dump battery service", e);
Dan Egnor18e93962010-02-10 19:27:58 -0800654 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800655 Slog.e(TAG, "failed to write dumpsys file", e);
Dan Egnor18e93962010-02-10 19:27:58 -0800656 } finally {
657 // make sure we clean up
658 if (dumpStream != null) {
659 try {
660 dumpStream.close();
661 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800662 Slog.e(TAG, "failed to close dumpsys output stream");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800663 }
Dan Egnor18e93962010-02-10 19:27:58 -0800664 }
665 if (dumpFile != null && !dumpFile.delete()) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800666 Slog.e(TAG, "failed to delete temporary dumpsys file: "
Dan Egnor18e93962010-02-10 19:27:58 -0800667 + dumpFile.getAbsolutePath());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 }
669 }
670 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800671
Jeff Browna4d82042012-10-02 19:11:19 -0700672 private void logOutlierLocked(long duration) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 ContentResolver cr = mContext.getContentResolver();
Jeff Sharkey625239a2012-09-26 22:03:49 -0700674 String dischargeThresholdString = Settings.Global.getString(cr,
675 Settings.Global.BATTERY_DISCHARGE_THRESHOLD);
676 String durationThresholdString = Settings.Global.getString(cr,
677 Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800678
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 if (dischargeThresholdString != null && durationThresholdString != null) {
680 try {
681 long durationThreshold = Long.parseLong(durationThresholdString);
682 int dischargeThreshold = Integer.parseInt(dischargeThresholdString);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800683 if (duration <= durationThreshold &&
Yifan Honge41e9392017-11-06 12:56:59 -0800684 mDischargeStartLevel - mHealthInfo.batteryLevel >= dischargeThreshold) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800685 // If the discharge cycle is bad enough we want to know about it.
Jeff Browna4d82042012-10-02 19:11:19 -0700686 logBatteryStatsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800687 }
Jeff Browna4d82042012-10-02 19:11:19 -0700688 if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800689 " discharge threshold: " + dischargeThreshold);
Jeff Browna4d82042012-10-02 19:11:19 -0700690 if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +
Yifan Honge41e9392017-11-06 12:56:59 -0800691 (mDischargeStartLevel - mHealthInfo.batteryLevel));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800693 Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800694 durationThresholdString + " or " + dischargeThresholdString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 }
696 }
697 }
698
Jeff Browna4d82042012-10-02 19:11:19 -0700699 private int getIconLocked(int level) {
Yifan Honge41e9392017-11-06 12:56:59 -0800700 if (mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701 return com.android.internal.R.drawable.stat_sys_battery_charge;
Yifan Honge41e9392017-11-06 12:56:59 -0800702 } else if (mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703 return com.android.internal.R.drawable.stat_sys_battery;
Yifan Honge41e9392017-11-06 12:56:59 -0800704 } else if (mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
705 || mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
Jeff Browna4d82042012-10-02 19:11:19 -0700706 if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)
Yifan Honge41e9392017-11-06 12:56:59 -0800707 && mHealthInfo.batteryLevel >= 100) {
Joe Onorato794be402010-11-21 19:22:25 -0800708 return com.android.internal.R.drawable.stat_sys_battery_charge;
709 } else {
710 return com.android.internal.R.drawable.stat_sys_battery;
711 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 } else {
713 return com.android.internal.R.drawable.stat_sys_battery_unknown;
714 }
715 }
716
Dianne Hackborn2e441072015-10-28 18:00:57 -0700717 class Shell extends ShellCommand {
718 @Override
719 public int onCommand(String cmd) {
720 return onShellCommand(this, cmd);
721 }
722
723 @Override
724 public void onHelp() {
725 PrintWriter pw = getOutPrintWriter();
726 dumpHelp(pw);
727 }
728 }
729
730 static void dumpHelp(PrintWriter pw) {
731 pw.println("Battery service (battery) commands:");
732 pw.println(" help");
733 pw.println(" Print this help text.");
Adam Lesinski29ddfe52017-03-29 19:29:00 -0700734 pw.println(" set [-f] [ac|usb|wireless|status|level|temp|present|invalid] <value>");
Dianne Hackborn2e441072015-10-28 18:00:57 -0700735 pw.println(" Force a battery property value, freezing battery state.");
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800736 pw.println(" -f: force a battery change broadcast be sent, prints new sequence.");
737 pw.println(" unplug [-f]");
Dianne Hackborn2e441072015-10-28 18:00:57 -0700738 pw.println(" Force battery unplugged, freezing battery state.");
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800739 pw.println(" -f: force a battery change broadcast be sent, prints new sequence.");
740 pw.println(" reset [-f]");
Dianne Hackborn2e441072015-10-28 18:00:57 -0700741 pw.println(" Unfreeze battery state, returning to current hardware values.");
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800742 pw.println(" -f: force a battery change broadcast be sent, prints new sequence.");
743 }
744
745 static final int OPTION_FORCE_UPDATE = 1<<0;
746
747 int parseOptions(Shell shell) {
748 String opt;
749 int opts = 0;
750 while ((opt = shell.getNextOption()) != null) {
751 if ("-f".equals(opt)) {
752 opts |= OPTION_FORCE_UPDATE;
753 }
754 }
755 return opts;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700756 }
757
758 int onShellCommand(Shell shell, String cmd) {
759 if (cmd == null) {
760 return shell.handleDefaultCommands(cmd);
761 }
762 PrintWriter pw = shell.getOutPrintWriter();
763 switch (cmd) {
764 case "unplug": {
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800765 int opts = parseOptions(shell);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700766 getContext().enforceCallingOrSelfPermission(
767 android.Manifest.permission.DEVICE_POWER, null);
768 if (!mUpdatesStopped) {
Yifan Hong932190b2017-10-11 11:00:51 -0700769 copy(mLastHealthInfo, mHealthInfo);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700770 }
Yifan Honge41e9392017-11-06 12:56:59 -0800771 mHealthInfo.chargerAcOnline = false;
772 mHealthInfo.chargerUsbOnline = false;
773 mHealthInfo.chargerWirelessOnline = false;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700774 long ident = Binder.clearCallingIdentity();
775 try {
776 mUpdatesStopped = true;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800777 processValuesFromShellLocked(pw, opts);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700778 } finally {
779 Binder.restoreCallingIdentity(ident);
780 }
781 } break;
782 case "set": {
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800783 int opts = parseOptions(shell);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700784 getContext().enforceCallingOrSelfPermission(
785 android.Manifest.permission.DEVICE_POWER, null);
786 final String key = shell.getNextArg();
787 if (key == null) {
788 pw.println("No property specified");
789 return -1;
790
791 }
792 final String value = shell.getNextArg();
793 if (value == null) {
794 pw.println("No value specified");
795 return -1;
796
797 }
798 try {
799 if (!mUpdatesStopped) {
Yifan Hong932190b2017-10-11 11:00:51 -0700800 copy(mLastHealthInfo, mHealthInfo);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700801 }
802 boolean update = true;
803 switch (key) {
Christopher Tate630d98b2017-03-07 14:12:26 -0800804 case "present":
Yifan Honge41e9392017-11-06 12:56:59 -0800805 mHealthInfo.batteryPresent = Integer.parseInt(value) != 0;
Christopher Tate630d98b2017-03-07 14:12:26 -0800806 break;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700807 case "ac":
Yifan Honge41e9392017-11-06 12:56:59 -0800808 mHealthInfo.chargerAcOnline = Integer.parseInt(value) != 0;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700809 break;
810 case "usb":
Yifan Honge41e9392017-11-06 12:56:59 -0800811 mHealthInfo.chargerUsbOnline = Integer.parseInt(value) != 0;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700812 break;
813 case "wireless":
Yifan Honge41e9392017-11-06 12:56:59 -0800814 mHealthInfo.chargerWirelessOnline = Integer.parseInt(value) != 0;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700815 break;
816 case "status":
Yifan Honge41e9392017-11-06 12:56:59 -0800817 mHealthInfo.batteryStatus = Integer.parseInt(value);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700818 break;
819 case "level":
Yifan Honge41e9392017-11-06 12:56:59 -0800820 mHealthInfo.batteryLevel = Integer.parseInt(value);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700821 break;
Adam Lesinski29ddfe52017-03-29 19:29:00 -0700822 case "temp":
Yifan Honge41e9392017-11-06 12:56:59 -0800823 mHealthInfo.batteryTemperature = Integer.parseInt(value);
Adam Lesinski29ddfe52017-03-29 19:29:00 -0700824 break;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700825 case "invalid":
826 mInvalidCharger = Integer.parseInt(value);
827 break;
828 default:
829 pw.println("Unknown set option: " + key);
830 update = false;
831 break;
832 }
833 if (update) {
834 long ident = Binder.clearCallingIdentity();
835 try {
836 mUpdatesStopped = true;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800837 processValuesFromShellLocked(pw, opts);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700838 } finally {
839 Binder.restoreCallingIdentity(ident);
840 }
841 }
842 } catch (NumberFormatException ex) {
843 pw.println("Bad value: " + value);
844 return -1;
845 }
846 } break;
847 case "reset": {
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800848 int opts = parseOptions(shell);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700849 getContext().enforceCallingOrSelfPermission(
850 android.Manifest.permission.DEVICE_POWER, null);
851 long ident = Binder.clearCallingIdentity();
852 try {
853 if (mUpdatesStopped) {
854 mUpdatesStopped = false;
Yifan Hong932190b2017-10-11 11:00:51 -0700855 copy(mHealthInfo, mLastHealthInfo);
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800856 processValuesFromShellLocked(pw, opts);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700857 }
858 } finally {
859 Binder.restoreCallingIdentity(ident);
860 }
861 } break;
862 default:
863 return shell.handleDefaultCommands(cmd);
864 }
865 return 0;
866 }
867
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800868 private void processValuesFromShellLocked(PrintWriter pw, int opts) {
869 processValuesLocked((opts & OPTION_FORCE_UPDATE) != 0);
870 if ((opts & OPTION_FORCE_UPDATE) != 0) {
871 pw.println(mSequence);
872 }
873 }
874
Dianne Hackborn2e441072015-10-28 18:00:57 -0700875 private void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Browna4d82042012-10-02 19:11:19 -0700876 synchronized (mLock) {
877 if (args == null || args.length == 0 || "-a".equals(args[0])) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700878 pw.println("Current Battery Service state:");
Dianne Hackbornc428aae2012-10-03 16:38:22 -0700879 if (mUpdatesStopped) {
880 pw.println(" (UPDATES STOPPED -- use 'reset' to restart)");
881 }
Yifan Honge41e9392017-11-06 12:56:59 -0800882 pw.println(" AC powered: " + mHealthInfo.chargerAcOnline);
883 pw.println(" USB powered: " + mHealthInfo.chargerUsbOnline);
884 pw.println(" Wireless powered: " + mHealthInfo.chargerWirelessOnline);
885 pw.println(" Max charging current: " + mHealthInfo.maxChargingCurrent);
886 pw.println(" Max charging voltage: " + mHealthInfo.maxChargingVoltage);
887 pw.println(" Charge counter: " + mHealthInfo.batteryChargeCounter);
888 pw.println(" status: " + mHealthInfo.batteryStatus);
889 pw.println(" health: " + mHealthInfo.batteryHealth);
890 pw.println(" present: " + mHealthInfo.batteryPresent);
891 pw.println(" level: " + mHealthInfo.batteryLevel);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700892 pw.println(" scale: " + BATTERY_SCALE);
Yifan Honge41e9392017-11-06 12:56:59 -0800893 pw.println(" voltage: " + mHealthInfo.batteryVoltage);
894 pw.println(" temperature: " + mHealthInfo.batteryTemperature);
895 pw.println(" technology: " + mHealthInfo.batteryTechnology);
Dianne Hackbornc428aae2012-10-03 16:38:22 -0700896 } else {
Dianne Hackborn2e441072015-10-28 18:00:57 -0700897 Shell shell = new Shell();
Dianne Hackborn354736e2016-08-22 17:00:05 -0700898 shell.exec(mBinderService, null, fd, null, args, null, new ResultReceiver(null));
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700899 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900 }
901 }
Joe Onoratode1b3592010-10-25 20:36:47 -0700902
Netta Pe2a3cd82017-01-26 18:03:51 -0800903 private void dumpProto(FileDescriptor fd) {
904 final ProtoOutputStream proto = new ProtoOutputStream(fd);
905
906 synchronized (mLock) {
907 proto.write(BatteryServiceDumpProto.ARE_UPDATES_STOPPED, mUpdatesStopped);
Kweku Adamse6b00c22017-10-23 16:46:45 -0700908 int batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_NONE;
Yifan Honge41e9392017-11-06 12:56:59 -0800909 if (mHealthInfo.chargerAcOnline) {
Kweku Adamse6b00c22017-10-23 16:46:45 -0700910 batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_AC;
Yifan Honge41e9392017-11-06 12:56:59 -0800911 } else if (mHealthInfo.chargerUsbOnline) {
Kweku Adamse6b00c22017-10-23 16:46:45 -0700912 batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_USB;
Yifan Honge41e9392017-11-06 12:56:59 -0800913 } else if (mHealthInfo.chargerWirelessOnline) {
Kweku Adamse6b00c22017-10-23 16:46:45 -0700914 batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_WIRELESS;
Netta Pe2a3cd82017-01-26 18:03:51 -0800915 }
916 proto.write(BatteryServiceDumpProto.PLUGGED, batteryPluggedValue);
Yifan Honge41e9392017-11-06 12:56:59 -0800917 proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent);
918 proto.write(BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltage);
919 proto.write(BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
920 proto.write(BatteryServiceDumpProto.STATUS, mHealthInfo.batteryStatus);
921 proto.write(BatteryServiceDumpProto.HEALTH, mHealthInfo.batteryHealth);
922 proto.write(BatteryServiceDumpProto.IS_PRESENT, mHealthInfo.batteryPresent);
923 proto.write(BatteryServiceDumpProto.LEVEL, mHealthInfo.batteryLevel);
Netta Pe2a3cd82017-01-26 18:03:51 -0800924 proto.write(BatteryServiceDumpProto.SCALE, BATTERY_SCALE);
Yifan Honge41e9392017-11-06 12:56:59 -0800925 proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.batteryVoltage);
926 proto.write(BatteryServiceDumpProto.TEMPERATURE, mHealthInfo.batteryTemperature);
927 proto.write(BatteryServiceDumpProto.TECHNOLOGY, mHealthInfo.batteryTechnology);
Netta Pe2a3cd82017-01-26 18:03:51 -0800928 }
929 proto.flush();
930 }
931
Jeff Browna4d82042012-10-02 19:11:19 -0700932 private final class Led {
Adam Lesinskief2ea1f2013-12-05 16:48:06 -0800933 private final Light mBatteryLight;
Joe Onoratode1b3592010-10-25 20:36:47 -0700934
Jeff Browna4d82042012-10-02 19:11:19 -0700935 private final int mBatteryLowARGB;
936 private final int mBatteryMediumARGB;
937 private final int mBatteryFullARGB;
938 private final int mBatteryLedOn;
939 private final int mBatteryLedOff;
940
Adam Lesinskief2ea1f2013-12-05 16:48:06 -0800941 public Led(Context context, LightsManager lights) {
942 mBatteryLight = lights.getLight(LightsManager.LIGHT_ID_BATTERY);
Joe Onoratode1b3592010-10-25 20:36:47 -0700943
Jeff Browna4d82042012-10-02 19:11:19 -0700944 mBatteryLowARGB = context.getResources().getInteger(
Joe Onoratode1b3592010-10-25 20:36:47 -0700945 com.android.internal.R.integer.config_notificationsBatteryLowARGB);
Jeff Browna4d82042012-10-02 19:11:19 -0700946 mBatteryMediumARGB = context.getResources().getInteger(
Joe Onoratode1b3592010-10-25 20:36:47 -0700947 com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
Jeff Browna4d82042012-10-02 19:11:19 -0700948 mBatteryFullARGB = context.getResources().getInteger(
Joe Onoratode1b3592010-10-25 20:36:47 -0700949 com.android.internal.R.integer.config_notificationsBatteryFullARGB);
Jeff Browna4d82042012-10-02 19:11:19 -0700950 mBatteryLedOn = context.getResources().getInteger(
Joe Onoratode1b3592010-10-25 20:36:47 -0700951 com.android.internal.R.integer.config_notificationsBatteryLedOn);
Jeff Browna4d82042012-10-02 19:11:19 -0700952 mBatteryLedOff = context.getResources().getInteger(
Joe Onoratode1b3592010-10-25 20:36:47 -0700953 com.android.internal.R.integer.config_notificationsBatteryLedOff);
954 }
955
956 /**
957 * Synchronize on BatteryService.
958 */
Jeff Browna4d82042012-10-02 19:11:19 -0700959 public void updateLightsLocked() {
Yifan Honge41e9392017-11-06 12:56:59 -0800960 final int level = mHealthInfo.batteryLevel;
961 final int status = mHealthInfo.batteryStatus;
Joe Onoratode1b3592010-10-25 20:36:47 -0700962 if (level < mLowBatteryWarningLevel) {
963 if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
964 // Solid red when battery is charging
965 mBatteryLight.setColor(mBatteryLowARGB);
966 } else {
967 // Flash red when battery is low and not charging
Adam Lesinskief2ea1f2013-12-05 16:48:06 -0800968 mBatteryLight.setFlashing(mBatteryLowARGB, Light.LIGHT_FLASH_TIMED,
Joe Onoratode1b3592010-10-25 20:36:47 -0700969 mBatteryLedOn, mBatteryLedOff);
970 }
971 } else if (status == BatteryManager.BATTERY_STATUS_CHARGING
972 || status == BatteryManager.BATTERY_STATUS_FULL) {
973 if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
974 // Solid green when full or charging and nearly full
975 mBatteryLight.setColor(mBatteryFullARGB);
976 } else {
977 // Solid orange when charging and halfway full
978 mBatteryLight.setColor(mBatteryMediumARGB);
979 }
980 } else {
981 // No lights if not charging and not low
982 mBatteryLight.turnOff();
983 }
984 }
985 }
Todd Poynor26faecc2013-05-22 18:54:48 -0700986
Yifan Hong89d55c12017-10-11 11:29:01 -0700987 private final class HealthHalCallback extends IHealthInfoCallback.Stub
988 implements HealthServiceWrapper.Callback {
989 @Override public void healthInfoChanged(HealthInfo props) {
990 BatteryService.this.update(props);
991 }
992 // on new service registered
993 @Override public void onRegistration(IHealth oldService, IHealth newService,
994 String instance) {
995 if (newService == null) return;
996
Adam Lesinskief2ea1f2013-12-05 16:48:06 -0800997 try {
Yifan Hong89d55c12017-10-11 11:29:01 -0700998 if (oldService != null) {
999 int r = oldService.unregisterCallback(this);
1000 if (r != Result.SUCCESS) {
1001 Slog.w(TAG, "health: cannot unregister previous callback: " +
1002 Result.toString(r));
1003 }
1004 }
1005 } catch (RemoteException ex) {
1006 Slog.w(TAG, "health: cannot unregister previous callback (transaction error): "
1007 + ex.getMessage());
Adam Lesinskief2ea1f2013-12-05 16:48:06 -08001008 }
Yifan Hong89d55c12017-10-11 11:29:01 -07001009
1010 try {
1011 int r = newService.registerCallback(this);
1012 if (r != Result.SUCCESS) {
1013 Slog.w(TAG, "health: cannot register callback: " + Result.toString(r));
1014 return;
1015 }
1016 // registerCallback does NOT guarantee that update is called
1017 // immediately, so request a manual update here.
1018 newService.update();
1019 } catch (RemoteException ex) {
1020 Slog.e(TAG, "health: cannot register callback (transaction error): "
1021 + ex.getMessage());
1022 }
1023 }
Todd Poynor26faecc2013-05-22 18:54:48 -07001024 }
Jeff Brown21392762014-06-13 19:00:36 -07001025
1026 private final class BinderService extends Binder {
Dianne Hackborn2e441072015-10-28 18:00:57 -07001027 @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -06001028 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
Jeff Brown21392762014-06-13 19:00:36 -07001029
Netta Pe2a3cd82017-01-26 18:03:51 -08001030 if (args.length > 0 && "--proto".equals(args[0])) {
1031 dumpProto(fd);
1032 } else {
1033 dumpInternal(fd, pw, args);
1034 }
Dianne Hackborn2e441072015-10-28 18:00:57 -07001035 }
1036
1037 @Override public void onShellCommand(FileDescriptor in, FileDescriptor out,
Dianne Hackborn354736e2016-08-22 17:00:05 -07001038 FileDescriptor err, String[] args, ShellCallback callback,
1039 ResultReceiver resultReceiver) {
1040 (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
Jeff Brown21392762014-06-13 19:00:36 -07001041 }
1042 }
1043
Yifan Hong1fd86f4c2017-10-09 16:50:33 -07001044 // Reduced IBatteryPropertiesRegistrar that only implements getProperty for usage
1045 // in BatteryManager.
1046 private final class BatteryPropertiesRegistrar extends IBatteryPropertiesRegistrar.Stub {
1047 public void registerListener(IBatteryPropertiesListener listener) {
1048 Slog.e(TAG, "health: must not call registerListener on battery properties");
1049 }
1050 public void unregisterListener(IBatteryPropertiesListener listener) {
1051 Slog.e(TAG, "health: must not call unregisterListener on battery properties");
1052 }
1053 public int getProperty(int id, final BatteryProperty prop) throws RemoteException {
1054 IHealth service = mHealthServiceWrapper.getLastService();
Yifan Hong7838fcb2017-10-24 16:06:27 -07001055 if (service == null) throw new RemoteException("no health service");
Yifan Hong1fd86f4c2017-10-09 16:50:33 -07001056 final MutableInt outResult = new MutableInt(Result.NOT_SUPPORTED);
1057 switch(id) {
1058 case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER:
1059 service.getChargeCounter((int result, int value) -> {
1060 outResult.value = result;
1061 if (result == Result.SUCCESS) prop.setLong(value);
1062 });
1063 break;
1064 case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW:
1065 service.getCurrentNow((int result, int value) -> {
1066 outResult.value = result;
1067 if (result == Result.SUCCESS) prop.setLong(value);
1068 });
1069 break;
1070 case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE:
1071 service.getCurrentAverage((int result, int value) -> {
1072 outResult.value = result;
1073 if (result == Result.SUCCESS) prop.setLong(value);
1074 });
1075 break;
1076 case BatteryManager.BATTERY_PROPERTY_CAPACITY:
1077 service.getCapacity((int result, int value) -> {
1078 outResult.value = result;
1079 if (result == Result.SUCCESS) prop.setLong(value);
1080 });
1081 break;
1082 case BatteryManager.BATTERY_PROPERTY_STATUS:
1083 service.getChargeStatus((int result, int value) -> {
1084 outResult.value = result;
1085 if (result == Result.SUCCESS) prop.setLong(value);
1086 });
1087 break;
1088 case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER:
1089 service.getEnergyCounter((int result, long value) -> {
1090 outResult.value = result;
1091 if (result == Result.SUCCESS) prop.setLong(value);
1092 });
1093 break;
1094 }
1095 return outResult.value;
1096 }
Yifan Hong7838fcb2017-10-24 16:06:27 -07001097 public void scheduleUpdate() throws RemoteException {
1098 IHealth service = mHealthServiceWrapper.getLastService();
1099 if (service == null) throw new RemoteException("no health service");
1100 service.update();
Yifan Hong1fd86f4c2017-10-09 16:50:33 -07001101 }
1102 }
1103
Jeff Brown21392762014-06-13 19:00:36 -07001104 private final class LocalService extends BatteryManagerInternal {
1105 @Override
1106 public boolean isPowered(int plugTypeSet) {
1107 synchronized (mLock) {
1108 return isPoweredLocked(plugTypeSet);
1109 }
1110 }
1111
1112 @Override
1113 public int getPlugType() {
1114 synchronized (mLock) {
1115 return mPlugType;
1116 }
1117 }
1118
1119 @Override
1120 public int getBatteryLevel() {
1121 synchronized (mLock) {
Yifan Honge41e9392017-11-06 12:56:59 -08001122 return mHealthInfo.batteryLevel;
Jeff Brown21392762014-06-13 19:00:36 -07001123 }
1124 }
1125
1126 @Override
1127 public boolean getBatteryLevelLow() {
1128 synchronized (mLock) {
1129 return mBatteryLevelLow;
1130 }
1131 }
1132
1133 @Override
1134 public int getInvalidCharger() {
1135 synchronized (mLock) {
1136 return mInvalidCharger;
1137 }
1138 }
1139 }
Yifan Hong98852792017-10-12 11:35:14 -07001140
1141 /**
1142 * HealthServiceWrapper wraps the internal IHealth service and refreshes the service when
1143 * necessary.
1144 *
1145 * On new registration of IHealth service, {@link #onRegistration onRegistration} is called and
1146 * the internal service is refreshed.
1147 * On death of an existing IHealth service, the internal service is NOT cleared to avoid
1148 * race condition between death notification and new service notification. Hence,
1149 * a caller must check for transaction errors when calling into the service.
1150 *
1151 * @hide Should only be used internally.
1152 */
1153 @VisibleForTesting
1154 static final class HealthServiceWrapper {
1155 private static final String TAG = "HealthServiceWrapper";
1156 public static final String INSTANCE_HEALTHD = "backup";
1157 public static final String INSTANCE_VENDOR = "default";
1158 // All interesting instances, sorted by priority high -> low.
1159 private static final List<String> sAllInstances =
1160 Arrays.asList(INSTANCE_VENDOR, INSTANCE_HEALTHD);
1161
1162 private final IServiceNotification mNotification = new Notification();
Yifan Hong1fff9842017-10-27 13:53:10 -07001163 // These variables are fixed after init.
Yifan Hong98852792017-10-12 11:35:14 -07001164 private Callback mCallback;
1165 private IHealthSupplier mHealthSupplier;
Yifan Hong1fff9842017-10-27 13:53:10 -07001166 private String mInstanceName;
Yifan Hong98852792017-10-12 11:35:14 -07001167
Yifan Hong89d55c12017-10-11 11:29:01 -07001168 private final Object mLastServiceSetLock = new Object();
1169 // Last IHealth service received.
1170 // set must be also be guarded with mLastServiceSetLock to ensure ordering.
1171 private final AtomicReference<IHealth> mLastService = new AtomicReference<>();
1172
Yifan Hong98852792017-10-12 11:35:14 -07001173 /**
1174 * init should be called after constructor. For testing purposes, init is not called by
1175 * constructor.
1176 */
1177 HealthServiceWrapper() {
1178 }
1179
Yifan Hong1fd86f4c2017-10-09 16:50:33 -07001180 IHealth getLastService() {
1181 return mLastService.get();
1182 }
1183
Yifan Hong98852792017-10-12 11:35:14 -07001184 /**
1185 * Start monitoring registration of new IHealth services. Only instances that are in
1186 * {@code sAllInstances} and in device / framework manifest are used. This function should
1187 * only be called once.
1188 * @throws RemoteException transaction error when talking to IServiceManager
1189 * @throws NoSuchElementException if one of the following cases:
1190 * - No service manager;
1191 * - none of {@code sAllInstances} are in manifests (i.e. not
1192 * available on this device), or none of these instances are available to current
1193 * process.
1194 * @throws NullPointerException when callback is null or supplier is null
1195 */
1196 void init(Callback callback,
1197 IServiceManagerSupplier managerSupplier,
1198 IHealthSupplier healthSupplier)
1199 throws RemoteException, NoSuchElementException, NullPointerException {
1200 if (callback == null || managerSupplier == null || healthSupplier == null)
1201 throw new NullPointerException();
1202
1203 mCallback = callback;
1204 mHealthSupplier = healthSupplier;
1205
1206 IServiceManager manager = managerSupplier.get();
1207 for (String name : sAllInstances) {
Yifan Hong1fff9842017-10-27 13:53:10 -07001208 if (manager.getTransport(IHealth.kInterfaceName, name) !=
Yifan Hong98852792017-10-12 11:35:14 -07001209 IServiceManager.Transport.EMPTY) {
Yifan Hong1fff9842017-10-27 13:53:10 -07001210 mInstanceName = name;
1211 break;
Yifan Hong98852792017-10-12 11:35:14 -07001212 }
Yifan Hong98852792017-10-12 11:35:14 -07001213 }
1214
Yifan Hong1fff9842017-10-27 13:53:10 -07001215 if (mInstanceName == null) {
1216 throw new NoSuchElementException(String.format(
1217 "No IHealth service instance among %s is available. Perhaps no permission?",
1218 sAllInstances.toString()));
1219 }
1220
1221 manager.registerForNotifications(IHealth.kInterfaceName, mInstanceName, mNotification);
1222 Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + mInstanceName);
Yifan Hong98852792017-10-12 11:35:14 -07001223 }
1224
1225 interface Callback {
1226 /**
1227 * This function is invoked asynchronously when a new and related IServiceNotification
1228 * is received.
1229 * @param service the recently retrieved service from IServiceManager.
1230 * Can be a dead service before service notification of a new service is delivered.
1231 * Implementation must handle cases for {@link RemoteException}s when calling
1232 * into service.
1233 * @param instance instance name.
1234 */
Yifan Hong89d55c12017-10-11 11:29:01 -07001235 void onRegistration(IHealth oldService, IHealth newService, String instance);
Yifan Hong98852792017-10-12 11:35:14 -07001236 }
1237
1238 /**
1239 * Supplier of services.
1240 * Must not return null; throw {@link NoSuchElementException} if a service is not available.
1241 */
1242 interface IServiceManagerSupplier {
Yifan Hong89d55c12017-10-11 11:29:01 -07001243 default IServiceManager get() throws NoSuchElementException, RemoteException {
1244 return IServiceManager.getService();
1245 }
Yifan Hong98852792017-10-12 11:35:14 -07001246 }
1247 /**
1248 * Supplier of services.
1249 * Must not return null; throw {@link NoSuchElementException} if a service is not available.
1250 */
1251 interface IHealthSupplier {
Yifan Hong89d55c12017-10-11 11:29:01 -07001252 default IHealth get(String name) throws NoSuchElementException, RemoteException {
1253 return IHealth.getService(name);
1254 }
Yifan Hong98852792017-10-12 11:35:14 -07001255 }
1256
1257 private class Notification extends IServiceNotification.Stub {
1258 @Override
1259 public final void onRegistration(String interfaceName, String instanceName,
1260 boolean preexisting) {
1261 if (!IHealth.kInterfaceName.equals(interfaceName)) return;
Yifan Hong1fff9842017-10-27 13:53:10 -07001262 if (!mInstanceName.equals(instanceName)) return;
Yifan Hong98852792017-10-12 11:35:14 -07001263 try {
Yifan Hong89d55c12017-10-11 11:29:01 -07001264 // ensures the order of multiple onRegistration on different threads.
1265 synchronized (mLastServiceSetLock) {
1266 IHealth newService = mHealthSupplier.get(instanceName);
1267 IHealth oldService = mLastService.getAndSet(newService);
1268 Slog.i(TAG, "health: new instance registered " + instanceName);
1269 mCallback.onRegistration(oldService, newService, instanceName);
1270 }
Yifan Hong98852792017-10-12 11:35:14 -07001271 } catch (NoSuchElementException | RemoteException ex) {
1272 Slog.e(TAG, "health: Cannot get instance '" + instanceName + "': " +
1273 ex.getMessage() + ". Perhaps no permission?");
1274 }
1275 }
1276 }
1277 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001278}