blob: a33fcd5573694169507f7dc3c450d75fb7d8c27d [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
Beverlyc4eb9342018-05-10 15:46:03 -040019import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020
Sudheer Shankadc589ac2016-11-10 15:30:17 -080021import android.app.ActivityManager;
Beverlyc4eb9342018-05-10 15:46:03 -040022import android.app.ActivityManagerInternal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.ContentResolver;
24import android.content.Context;
25import android.content.Intent;
Beverlyc4eb9342018-05-10 15:46:03 -040026import android.database.ContentObserver;
27import android.hardware.health.V1_0.HealthInfo;
28import android.hardware.health.V2_0.IHealth;
29import android.hardware.health.V2_0.IHealthInfoCallback;
30import android.hardware.health.V2_0.Result;
Yifan Hong98852792017-10-12 11:35:14 -070031import android.hidl.manager.V1_0.IServiceManager;
32import android.hidl.manager.V1_0.IServiceNotification;
Beverlyc4eb9342018-05-10 15:46:03 -040033import android.metrics.LogMaker;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.os.BatteryManager;
Jeff Brown21392762014-06-13 19:00:36 -070035import android.os.BatteryManagerInternal;
Yifan Hong1fd86f4c2017-10-09 16:50:33 -070036import android.os.BatteryProperty;
Beverlyc4eb9342018-05-10 15:46:03 -040037import android.os.BatteryStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.os.Binder;
Beverlyc4eb9342018-05-10 15:46:03 -040039import android.os.Bundle;
Bookatz1a1b0462018-01-12 11:47:03 -080040import android.os.DropBoxManager;
Dianne Hackborn8bdf5932010-10-15 12:54:40 -070041import android.os.FileUtils;
Jeff Brown605ea692012-10-05 16:33:10 -070042import android.os.Handler;
Yifan Hongcf9a8b22017-11-02 18:43:06 -070043import android.os.HandlerThread;
Todd Poynor26faecc2013-05-22 18:54:48 -070044import android.os.IBatteryPropertiesRegistrar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.os.IBinder;
Bookatz1a1b0462018-01-12 11:47:03 -080046import android.os.OsProtoEnums;
Beverlyc4eb9342018-05-10 15:46:03 -040047import android.os.PowerManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.os.RemoteException;
Beverlyc4eb9342018-05-10 15:46:03 -040049import android.os.ResultReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.os.ServiceManager;
Beverlyc4eb9342018-05-10 15:46:03 -040051import android.os.ShellCallback;
52import android.os.ShellCommand;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.os.SystemClock;
Yifan Hong8cc18ef2017-10-31 12:27:47 -070054import android.os.Trace;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.os.UEventObserver;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070056import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.provider.Settings;
Netta Pe2a3cd82017-01-26 18:03:51 -080058import android.service.battery.BatteryServiceDumpProto;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059import android.util.EventLog;
Yifan Hong1fd86f4c2017-10-09 16:50:33 -070060import android.util.MutableInt;
Joe Onorato8a9b2202010-02-26 18:56:32 -080061import android.util.Slog;
Netta Pe2a3cd82017-01-26 18:03:51 -080062import android.util.proto.ProtoOutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063
Beverlyc4eb9342018-05-10 15:46:03 -040064import com.android.internal.annotations.VisibleForTesting;
65import com.android.internal.app.IBatteryStats;
66import com.android.internal.logging.MetricsLogger;
67import com.android.internal.util.DumpUtils;
68import com.android.server.am.BatteryStatsService;
69import com.android.server.lights.Light;
70import com.android.server.lights.LightsManager;
71
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072import java.io.File;
73import java.io.FileDescriptor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074import java.io.FileOutputStream;
75import java.io.IOException;
76import java.io.PrintWriter;
Fyodor Kupolov70e75432018-03-01 18:29:06 -080077import java.util.ArrayDeque;
78import java.util.ArrayList;
Yifan Hong98852792017-10-12 11:35:14 -070079import java.util.Arrays;
80import java.util.List;
81import java.util.NoSuchElementException;
Yifan Hongcf9a8b22017-11-02 18:43:06 -070082import java.util.Objects;
Yifan Hong89d55c12017-10-11 11:29:01 -070083import java.util.concurrent.atomic.AtomicReference;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084
85/**
86 * <p>BatteryService monitors the charging status, and charge level of the device
87 * battery. When these values change this service broadcasts the new values
88 * to all {@link android.content.BroadcastReceiver IntentReceivers} that are
89 * watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED
90 * BATTERY_CHANGED} action.</p>
91 * <p>The new values are stored in the Intent data and can be retrieved by
92 * calling {@link android.content.Intent#getExtra Intent.getExtra} with the
93 * following keys:</p>
94 * <p>&quot;scale&quot; - int, the maximum value for the charge level</p>
95 * <p>&quot;level&quot; - int, charge level, from 0 through &quot;scale&quot; inclusive</p>
96 * <p>&quot;status&quot; - String, the current charging status.<br />
97 * <p>&quot;health&quot; - String, the current battery health.<br />
98 * <p>&quot;present&quot; - boolean, true if the battery is present<br />
99 * <p>&quot;icon-small&quot; - int, suggested small icon to use for this state</p>
100 * <p>&quot;plugged&quot; - int, 0 if the device is not plugged in; 1 if plugged
101 * into an AC power adapter; 2 if plugged in via USB.</p>
102 * <p>&quot;voltage&quot; - int, current battery voltage in millivolts</p>
103 * <p>&quot;temperature&quot; - int, current battery temperature in tenths of
104 * a degree Centigrade</p>
105 * <p>&quot;technology&quot; - String, the type of battery installed, e.g. "Li-ion"</p>
Jeff Brown605ea692012-10-05 16:33:10 -0700106 *
107 * <p>
108 * The battery service may be called by the power manager while holding its locks so
109 * we take care to post all outcalls into the activity manager to a handler.
110 *
111 * FIXME: Ideally the power manager would perform all of its calls into the battery
112 * service asynchronously itself.
113 * </p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 */
Jeff Brown21392762014-06-13 19:00:36 -0700115public final class BatteryService extends SystemService {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 private static final String TAG = BatteryService.class.getSimpleName();
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800117
Jeff Browna4d82042012-10-02 19:11:19 -0700118 private static final boolean DEBUG = false;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800119
Jeff Browna4d82042012-10-02 19:11:19 -0700120 private static final int BATTERY_SCALE = 100; // battery capacity is a percentage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121
Yifan Hong89d55c12017-10-11 11:29:01 -0700122 private static final long HEALTH_HAL_WAIT_MS = 1000;
Fyodor Kupolov70e75432018-03-01 18:29:06 -0800123 private static final long BATTERY_LEVEL_CHANGE_THROTTLE_MS = 60_000;
124 private static final int MAX_BATTERY_LEVELS_QUEUE_SIZE = 100;
Yifan Hong89d55c12017-10-11 11:29:01 -0700125
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 // Used locally for determining when to make a last ditch effort to log
127 // discharge stats before the device dies.
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700128 private int mCriticalBatteryLevel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129
Mike Ma99e57c32018-03-15 14:40:37 -0700130 // TODO: Current args don't work since "--unplugged" flag was purposefully removed.
Jeff Sharkeyec43a6b2013-04-30 13:33:18 -0700131 private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" };
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800132
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 private static final String DUMPSYS_DATA_PATH = "/data/system/";
134
135 // This should probably be exposed in the API, though it's not critical
Bookatz1a1b0462018-01-12 11:47:03 -0800136 private static final int BATTERY_PLUGGED_NONE = OsProtoEnums.BATTERY_PLUGGED_NONE; // = 0
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137
138 private final Context mContext;
139 private final IBatteryStats mBatteryStats;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700140 BinderService mBinderService;
Jeff Brown605ea692012-10-05 16:33:10 -0700141 private final Handler mHandler;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800142
Jeff Browna4d82042012-10-02 19:11:19 -0700143 private final Object mLock = new Object();
144
Yifan Hong932190b2017-10-11 11:00:51 -0700145 private HealthInfo mHealthInfo;
146 private final HealthInfo mLastHealthInfo = new HealthInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 private boolean mBatteryLevelCritical;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 private int mLastBatteryStatus;
149 private int mLastBatteryHealth;
150 private boolean mLastBatteryPresent;
151 private int mLastBatteryLevel;
152 private int mLastBatteryVoltage;
153 private int mLastBatteryTemperature;
154 private boolean mLastBatteryLevelCritical;
Adrian Roos76dc5a52015-07-21 16:20:36 -0700155 private int mLastMaxChargingCurrent;
Badhri Jagan Sridharanf92fcfe2015-10-27 13:59:34 -0700156 private int mLastMaxChargingVoltage;
Ruchi Kandoi6361e222016-04-07 11:28:30 -0700157 private int mLastChargeCounter;
Jeff Browna4d82042012-10-02 19:11:19 -0700158
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800159 private int mSequence = 1;
160
Jeff Browna4d82042012-10-02 19:11:19 -0700161 private int mInvalidCharger;
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700162 private int mLastInvalidCharger;
Mike Lockwoodd81b1f42009-09-25 09:32:19 -0400163
164 private int mLowBatteryWarningLevel;
165 private int mLowBatteryCloseWarningLevel;
Brian Muramatsuf3c74f32012-08-31 15:14:48 -0700166 private int mShutdownBatteryTemperature;
Mike Lockwoodd81b1f42009-09-25 09:32:19 -0400167
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 private int mPlugType;
169 private int mLastPlugType = -1; // Extra state so we can detect first run
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800170
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700171 private boolean mBatteryLevelLow;
172
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 private long mDischargeStartTime;
174 private int mDischargeStartLevel;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800175
Beverlyc4eb9342018-05-10 15:46:03 -0400176 private long mChargeStartTime;
177 private int mChargeStartLevel;
178
Dianne Hackbornc428aae2012-10-03 16:38:22 -0700179 private boolean mUpdatesStopped;
180
Joe Onoratode1b3592010-10-25 20:36:47 -0700181 private Led mLed;
182
Dianne Hackborn8ec5b832009-07-01 21:19:35 -0700183 private boolean mSentLowBatteryBroadcast = false;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800184
Sudheer Shankafc46e9b2016-10-21 17:55:27 -0700185 private ActivityManagerInternal mActivityManagerInternal;
186
Yifan Hong89d55c12017-10-11 11:29:01 -0700187 private HealthServiceWrapper mHealthServiceWrapper;
188 private HealthHalCallback mHealthHalCallback;
Yifan Hong1fd86f4c2017-10-09 16:50:33 -0700189 private BatteryPropertiesRegistrar mBatteryPropertiesRegistrar;
Fyodor Kupolov70e75432018-03-01 18:29:06 -0800190 private ArrayDeque<Bundle> mBatteryLevelsEventQueue;
191 private long mLastBatteryLevelChangedSentMs;
Yifan Hong89d55c12017-10-11 11:29:01 -0700192
Beverlyc4eb9342018-05-10 15:46:03 -0400193 private MetricsLogger mMetricsLogger;
194
Jeff Brown21392762014-06-13 19:00:36 -0700195 public BatteryService(Context context) {
196 super(context);
197
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 mContext = context;
Jeff Brown605ea692012-10-05 16:33:10 -0700199 mHandler = new Handler(true /*async*/);
Jeff Brown21392762014-06-13 19:00:36 -0700200 mLed = new Led(context, getLocalService(LightsManager.class));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 mBatteryStats = BatteryStatsService.getService();
Sudheer Shankafc46e9b2016-10-21 17:55:27 -0700202 mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700204 mCriticalBatteryLevel = mContext.getResources().getInteger(
205 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
Mike Lockwoodd81b1f42009-09-25 09:32:19 -0400206 mLowBatteryWarningLevel = mContext.getResources().getInteger(
207 com.android.internal.R.integer.config_lowBatteryWarningLevel);
Dianne Hackborn14272302014-06-10 23:13:02 -0700208 mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
209 com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
Brian Muramatsuf3c74f32012-08-31 15:14:48 -0700210 mShutdownBatteryTemperature = mContext.getResources().getInteger(
211 com.android.internal.R.integer.config_shutdownBatteryTemperature);
Mike Lockwoodd81b1f42009-09-25 09:32:19 -0400212
Fyodor Kupolov70e75432018-03-01 18:29:06 -0800213 mBatteryLevelsEventQueue = new ArrayDeque<>();
Beverlyc4eb9342018-05-10 15:46:03 -0400214 mMetricsLogger = new MetricsLogger();
Fyodor Kupolov70e75432018-03-01 18:29:06 -0800215
Mike Lockwooddeff9c82010-09-04 10:29:17 -0400216 // watch for invalid charger messages if the invalid_charger switch exists
217 if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
Dianne Hackborn2e441072015-10-28 18:00:57 -0700218 UEventObserver invalidChargerObserver = new UEventObserver() {
219 @Override
220 public void onUEvent(UEvent event) {
221 final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
222 synchronized (mLock) {
223 if (mInvalidCharger != invalidCharger) {
224 mInvalidCharger = invalidCharger;
225 }
226 }
227 }
228 };
229 invalidChargerObserver.startObserving(
Jeff Browna4d82042012-10-02 19:11:19 -0700230 "DEVPATH=/devices/virtual/switch/invalid_charger");
Mike Lockwooddeff9c82010-09-04 10:29:17 -0400231 }
Jeff Brown21392762014-06-13 19:00:36 -0700232 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233
Jeff Brown21392762014-06-13 19:00:36 -0700234 @Override
235 public void onStart() {
Yifan Hong89d55c12017-10-11 11:29:01 -0700236 registerHealthCallback();
Jeff Brown21392762014-06-13 19:00:36 -0700237
Dianne Hackborn2e441072015-10-28 18:00:57 -0700238 mBinderService = new BinderService();
239 publishBinderService("battery", mBinderService);
Yifan Hong1fd86f4c2017-10-09 16:50:33 -0700240 mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
241 publishBinderService("batteryproperties", mBatteryPropertiesRegistrar);
Jeff Brown21392762014-06-13 19:00:36 -0700242 publishLocalService(BatteryManagerInternal.class, new LocalService());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 }
244
Jeff Brown21392762014-06-13 19:00:36 -0700245 @Override
246 public void onBootPhase(int phase) {
247 if (phase == PHASE_ACTIVITY_MANAGER_READY) {
248 // check our power situation now that it is safe to display the shutdown dialog.
249 synchronized (mLock) {
250 ContentObserver obs = new ContentObserver(mHandler) {
251 @Override
252 public void onChange(boolean selfChange) {
253 synchronized (mLock) {
254 updateBatteryWarningLevelLocked();
255 }
Dianne Hackborn14272302014-06-10 23:13:02 -0700256 }
Jeff Brown21392762014-06-13 19:00:36 -0700257 };
258 final ContentResolver resolver = mContext.getContentResolver();
259 resolver.registerContentObserver(Settings.Global.getUriFor(
260 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
261 false, obs, UserHandle.USER_ALL);
262 updateBatteryWarningLevelLocked();
263 }
Jeff Browna4d82042012-10-02 19:11:19 -0700264 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 }
266
Yifan Hong89d55c12017-10-11 11:29:01 -0700267 private void registerHealthCallback() {
Yifan Hong8cc18ef2017-10-31 12:27:47 -0700268 traceBegin("HealthInitWrapper");
Yifan Hong89d55c12017-10-11 11:29:01 -0700269 mHealthServiceWrapper = new HealthServiceWrapper();
270 mHealthHalCallback = new HealthHalCallback();
271 // IHealth is lazily retrieved.
272 try {
273 mHealthServiceWrapper.init(mHealthHalCallback,
274 new HealthServiceWrapper.IServiceManagerSupplier() {},
275 new HealthServiceWrapper.IHealthSupplier() {});
Yifan Hong78e8af12017-10-26 15:57:19 -0700276 } catch (RemoteException ex) {
277 Slog.e(TAG, "health: cannot register callback. (RemoteException)");
278 throw ex.rethrowFromSystemServer();
279 } catch (NoSuchElementException ex) {
280 Slog.e(TAG, "health: cannot register callback. (no supported health HAL service)");
281 throw ex;
Yifan Hong8cc18ef2017-10-31 12:27:47 -0700282 } finally {
283 traceEnd();
Yifan Hong89d55c12017-10-11 11:29:01 -0700284 }
285
Yifan Hong8cc18ef2017-10-31 12:27:47 -0700286 traceBegin("HealthInitWaitUpdate");
Yifan Hong89d55c12017-10-11 11:29:01 -0700287 // init register for new service notifications, and IServiceManager should return the
288 // existing service in a near future. Wait for this.update() to instantiate
289 // the initial mHealthInfo.
Yifan Hong78e8af12017-10-26 15:57:19 -0700290 long beforeWait = SystemClock.uptimeMillis();
Yifan Hong89d55c12017-10-11 11:29:01 -0700291 synchronized (mLock) {
Yifan Hong78e8af12017-10-26 15:57:19 -0700292 while (mHealthInfo == null) {
293 Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait) +
294 "ms for callbacks. Waiting another " + HEALTH_HAL_WAIT_MS + " ms...");
Yifan Hong89d55c12017-10-11 11:29:01 -0700295 try {
Yifan Hong78e8af12017-10-26 15:57:19 -0700296 mLock.wait(HEALTH_HAL_WAIT_MS);
Yifan Hong89d55c12017-10-11 11:29:01 -0700297 } catch (InterruptedException ex) {
Yifan Hong78e8af12017-10-26 15:57:19 -0700298 Slog.i(TAG, "health: InterruptedException when waiting for update. "
299 + " Continuing...");
Yifan Hong89d55c12017-10-11 11:29:01 -0700300 }
301 }
Yifan Hong89d55c12017-10-11 11:29:01 -0700302 }
303
Yifan Hong78e8af12017-10-26 15:57:19 -0700304 Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait)
305 + "ms and received the update.");
Yifan Hong8cc18ef2017-10-31 12:27:47 -0700306 traceEnd();
Yifan Hong89d55c12017-10-11 11:29:01 -0700307 }
308
Jeff Brown21392762014-06-13 19:00:36 -0700309 private void updateBatteryWarningLevelLocked() {
Dianne Hackborn14272302014-06-10 23:13:02 -0700310 final ContentResolver resolver = mContext.getContentResolver();
311 int defWarnLevel = mContext.getResources().getInteger(
312 com.android.internal.R.integer.config_lowBatteryWarningLevel);
313 mLowBatteryWarningLevel = Settings.Global.getInt(resolver,
314 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
315 if (mLowBatteryWarningLevel == 0) {
316 mLowBatteryWarningLevel = defWarnLevel;
317 }
318 if (mLowBatteryWarningLevel < mCriticalBatteryLevel) {
319 mLowBatteryWarningLevel = mCriticalBatteryLevel;
320 }
321 mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
322 com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
323 processValuesLocked(true);
324 }
325
Jeff Browna4d82042012-10-02 19:11:19 -0700326 private boolean isPoweredLocked(int plugTypeSet) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 // assume we are powered if battery state is unknown so
328 // the "stay on while plugged in" option will work.
Yifan Honge41e9392017-11-06 12:56:59 -0800329 if (mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 return true;
331 }
Yifan Honge41e9392017-11-06 12:56:59 -0800332 if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mHealthInfo.chargerAcOnline) {
Jeff Browna4d82042012-10-02 19:11:19 -0700333 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 }
Yifan Honge41e9392017-11-06 12:56:59 -0800335 if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mHealthInfo.chargerUsbOnline) {
Jeff Browna4d82042012-10-02 19:11:19 -0700336 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 }
Yifan Honge41e9392017-11-06 12:56:59 -0800338 if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mHealthInfo.chargerWirelessOnline) {
Jeff Browna4d82042012-10-02 19:11:19 -0700339 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 }
Jeff Browna4d82042012-10-02 19:11:19 -0700341 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 }
343
Jeff Brown21392762014-06-13 19:00:36 -0700344 private boolean shouldSendBatteryLowLocked() {
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700345 final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
346 final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
347
348 /* The ACTION_BATTERY_LOW broadcast is sent in these situations:
349 * - is just un-plugged (previously was plugged) and battery level is
350 * less than or equal to WARNING, or
351 * - is not plugged and battery level falls to WARNING boundary
352 * (becomes <= mLowBatteryWarningLevel).
353 */
354 return !plugged
Yifan Honge41e9392017-11-06 12:56:59 -0800355 && mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
356 && mHealthInfo.batteryLevel <= mLowBatteryWarningLevel
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700357 && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
358 }
359
Yifan Hong258c8af2019-02-13 16:00:43 -0800360 private boolean shouldShutdownLocked() {
361 if (mHealthInfo.batteryLevel > 0) {
362 return false;
363 }
364
365 // Battery-less devices should not shutdown.
366 if (!mHealthInfo.batteryPresent) {
367 return false;
368 }
369
370 // If battery state is not CHARGING, shutdown.
371 // - If battery present and state == unknown, this is an unexpected error state.
372 // - If level <= 0 and state == full, this is also an unexpected state
373 // - All other states (NOT_CHARGING, DISCHARGING) means it is not charging.
374 return mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_CHARGING;
375 }
376
Jeff Browna4d82042012-10-02 19:11:19 -0700377 private void shutdownIfNoPowerLocked() {
Mike Lockwood07a500f2009-08-12 09:56:44 -0400378 // shut down gracefully if our battery is critically low and we are not powered.
379 // wait until the system has booted before attempting to display the shutdown dialog.
Yifan Hong258c8af2019-02-13 16:00:43 -0800380 if (shouldShutdownLocked()) {
Jeff Brown605ea692012-10-05 16:33:10 -0700381 mHandler.post(new Runnable() {
382 @Override
383 public void run() {
Sudheer Shankafc46e9b2016-10-21 17:55:27 -0700384 if (mActivityManagerInternal.isSystemReady()) {
Jeff Brown605ea692012-10-05 16:33:10 -0700385 Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
386 intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
Sudheer Shanka292637f2017-09-25 10:36:23 -0700387 intent.putExtra(Intent.EXTRA_REASON,
388 PowerManager.SHUTDOWN_LOW_BATTERY);
Jeff Brown605ea692012-10-05 16:33:10 -0700389 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
390 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
391 }
392 }
393 });
Mike Lockwood07a500f2009-08-12 09:56:44 -0400394 }
395 }
396
Jeff Browna4d82042012-10-02 19:11:19 -0700397 private void shutdownIfOverTempLocked() {
Brian Muramatsuf3c74f32012-08-31 15:14:48 -0700398 // shut down gracefully if temperature is too high (> 68.0C by default)
399 // wait until the system has booted before attempting to display the
400 // shutdown dialog.
Yifan Honge41e9392017-11-06 12:56:59 -0800401 if (mHealthInfo.batteryTemperature > mShutdownBatteryTemperature) {
Jeff Brown605ea692012-10-05 16:33:10 -0700402 mHandler.post(new Runnable() {
403 @Override
404 public void run() {
Sudheer Shankafc46e9b2016-10-21 17:55:27 -0700405 if (mActivityManagerInternal.isSystemReady()) {
Jeff Brown605ea692012-10-05 16:33:10 -0700406 Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
407 intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
Sudheer Shanka292637f2017-09-25 10:36:23 -0700408 intent.putExtra(Intent.EXTRA_REASON,
409 PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE);
Jeff Brown605ea692012-10-05 16:33:10 -0700410 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
411 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
412 }
413 }
414 });
Eric Olsen6a362a92010-03-26 15:38:41 -0700415 }
416 }
417
Hridya Valsarajua09fa902018-01-17 23:04:46 -0800418 private void update(android.hardware.health.V2_0.HealthInfo info) {
Yifan Hong8cc18ef2017-10-31 12:27:47 -0700419 traceBegin("HealthInfoUpdate");
Kweku Adamse06e97b42018-08-22 17:25:37 -0700420
421 Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryChargeCounter",
422 info.legacy.batteryChargeCounter);
423 Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent",
424 info.legacy.batteryCurrent);
425
Todd Poynor26faecc2013-05-22 18:54:48 -0700426 synchronized (mLock) {
427 if (!mUpdatesStopped) {
Hridya Valsarajua09fa902018-01-17 23:04:46 -0800428 mHealthInfo = info.legacy;
Todd Poynor26faecc2013-05-22 18:54:48 -0700429 // Process the new values.
Dianne Hackborn14272302014-06-10 23:13:02 -0700430 processValuesLocked(false);
Yifan Hong89d55c12017-10-11 11:29:01 -0700431 mLock.notifyAll(); // for any waiters on new info
Dianne Hackborna1f1a3c2014-02-24 18:12:28 -0800432 } else {
Hridya Valsarajua09fa902018-01-17 23:04:46 -0800433 copy(mLastHealthInfo, info.legacy);
Todd Poynor26faecc2013-05-22 18:54:48 -0700434 }
Dianne Hackbornc428aae2012-10-03 16:38:22 -0700435 }
Yifan Hong8cc18ef2017-10-31 12:27:47 -0700436 traceEnd();
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700437 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800438
Yifan Hong932190b2017-10-11 11:00:51 -0700439 private static void copy(HealthInfo dst, HealthInfo src) {
Yifan Honge41e9392017-11-06 12:56:59 -0800440 dst.chargerAcOnline = src.chargerAcOnline;
441 dst.chargerUsbOnline = src.chargerUsbOnline;
442 dst.chargerWirelessOnline = src.chargerWirelessOnline;
443 dst.maxChargingCurrent = src.maxChargingCurrent;
444 dst.maxChargingVoltage = src.maxChargingVoltage;
445 dst.batteryStatus = src.batteryStatus;
446 dst.batteryHealth = src.batteryHealth;
447 dst.batteryPresent = src.batteryPresent;
448 dst.batteryLevel = src.batteryLevel;
449 dst.batteryVoltage = src.batteryVoltage;
450 dst.batteryTemperature = src.batteryTemperature;
451 dst.batteryCurrent = src.batteryCurrent;
452 dst.batteryCycleCount = src.batteryCycleCount;
453 dst.batteryFullCharge = src.batteryFullCharge;
454 dst.batteryChargeCounter = src.batteryChargeCounter;
455 dst.batteryTechnology = src.batteryTechnology;
Yifan Hong932190b2017-10-11 11:00:51 -0700456 }
457
Dianne Hackborn14272302014-06-10 23:13:02 -0700458 private void processValuesLocked(boolean force) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700459 boolean logOutlier = false;
460 long dischargeDuration = 0;
Joe Onoratoa7e4cf9b2009-07-28 18:18:20 -0700461
Todd Poynorb41df4422017-12-08 10:44:40 -0800462 mBatteryLevelCritical =
463 mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
464 && mHealthInfo.batteryLevel <= mCriticalBatteryLevel;
Yifan Honge41e9392017-11-06 12:56:59 -0800465 if (mHealthInfo.chargerAcOnline) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
Yifan Honge41e9392017-11-06 12:56:59 -0800467 } else if (mHealthInfo.chargerUsbOnline) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
Yifan Honge41e9392017-11-06 12:56:59 -0800469 } else if (mHealthInfo.chargerWirelessOnline) {
Brian Muramatsu37a37f42012-08-14 15:21:02 -0700470 mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471 } else {
472 mPlugType = BATTERY_PLUGGED_NONE;
473 }
Brian Muramatsuf3c74f32012-08-31 15:14:48 -0700474
Jeff Browna4d82042012-10-02 19:11:19 -0700475 if (DEBUG) {
476 Slog.d(TAG, "Processing new values: "
Yifan Hong932190b2017-10-11 11:00:51 -0700477 + "info=" + mHealthInfo
Jeff Browna4d82042012-10-02 19:11:19 -0700478 + ", mBatteryLevelCritical=" + mBatteryLevelCritical
479 + ", mPlugType=" + mPlugType);
480 }
481
Dianne Hackborn6b7b4842010-06-14 17:17:44 -0700482 // Let the battery stats keep track of the current level.
483 try {
Yifan Honge41e9392017-11-06 12:56:59 -0800484 mBatteryStats.setBatteryState(mHealthInfo.batteryStatus, mHealthInfo.batteryHealth,
485 mPlugType, mHealthInfo.batteryLevel, mHealthInfo.batteryTemperature,
486 mHealthInfo.batteryVoltage, mHealthInfo.batteryChargeCounter,
487 mHealthInfo.batteryFullCharge);
Dianne Hackborn6b7b4842010-06-14 17:17:44 -0700488 } catch (RemoteException e) {
489 // Should never happen.
490 }
Brian Muramatsuf3c74f32012-08-31 15:14:48 -0700491
Jeff Browna4d82042012-10-02 19:11:19 -0700492 shutdownIfNoPowerLocked();
493 shutdownIfOverTempLocked();
Dianne Hackborn6b7b4842010-06-14 17:17:44 -0700494
Yifan Honge41e9392017-11-06 12:56:59 -0800495 if (force || (mHealthInfo.batteryStatus != mLastBatteryStatus ||
496 mHealthInfo.batteryHealth != mLastBatteryHealth ||
497 mHealthInfo.batteryPresent != mLastBatteryPresent ||
498 mHealthInfo.batteryLevel != mLastBatteryLevel ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 mPlugType != mLastPlugType ||
Yifan Honge41e9392017-11-06 12:56:59 -0800500 mHealthInfo.batteryVoltage != mLastBatteryVoltage ||
501 mHealthInfo.batteryTemperature != mLastBatteryTemperature ||
502 mHealthInfo.maxChargingCurrent != mLastMaxChargingCurrent ||
503 mHealthInfo.maxChargingVoltage != mLastMaxChargingVoltage ||
504 mHealthInfo.batteryChargeCounter != mLastChargeCounter ||
Dianne Hackborn14272302014-06-10 23:13:02 -0700505 mInvalidCharger != mLastInvalidCharger)) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800506
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 if (mPlugType != mLastPlugType) {
508 if (mLastPlugType == BATTERY_PLUGGED_NONE) {
509 // discharging -> charging
Beverlyc4eb9342018-05-10 15:46:03 -0400510 mChargeStartLevel = mHealthInfo.batteryLevel;
511 mChargeStartTime = SystemClock.elapsedRealtime();
512
513 final LogMaker builder = new LogMaker(MetricsEvent.ACTION_CHARGE);
514 builder.setType(MetricsEvent.TYPE_ACTION);
515 builder.addTaggedData(MetricsEvent.FIELD_PLUG_TYPE, mPlugType);
516 builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_START,
517 mHealthInfo.batteryLevel);
518 mMetricsLogger.write(builder);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800519
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800520 // There's no value in this data unless we've discharged at least once and the
521 // battery level has changed; so don't log until it does.
Yifan Honge41e9392017-11-06 12:56:59 -0800522 if (mDischargeStartTime != 0 && mDischargeStartLevel != mHealthInfo.batteryLevel) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700523 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
524 logOutlier = true;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800525 EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
Yifan Honge41e9392017-11-06 12:56:59 -0800526 mDischargeStartLevel, mHealthInfo.batteryLevel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 // make sure we see a discharge event before logging again
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800528 mDischargeStartTime = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 }
530 } else if (mPlugType == BATTERY_PLUGGED_NONE) {
531 // charging -> discharging or we just powered up
532 mDischargeStartTime = SystemClock.elapsedRealtime();
Yifan Honge41e9392017-11-06 12:56:59 -0800533 mDischargeStartLevel = mHealthInfo.batteryLevel;
Beverlyc4eb9342018-05-10 15:46:03 -0400534
535 long chargeDuration = SystemClock.elapsedRealtime() - mChargeStartTime;
536 if (mChargeStartTime != 0 && chargeDuration != 0) {
537 final LogMaker builder = new LogMaker(MetricsEvent.ACTION_CHARGE);
538 builder.setType(MetricsEvent.TYPE_DISMISS);
539 builder.addTaggedData(MetricsEvent.FIELD_PLUG_TYPE, mLastPlugType);
540 builder.addTaggedData(MetricsEvent.FIELD_CHARGING_DURATION_MILLIS,
541 chargeDuration);
542 builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_START,
543 mChargeStartLevel);
544 builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_END,
545 mHealthInfo.batteryLevel);
546 mMetricsLogger.write(builder);
547 }
548 mChargeStartTime = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 }
550 }
Yifan Honge41e9392017-11-06 12:56:59 -0800551 if (mHealthInfo.batteryStatus != mLastBatteryStatus ||
552 mHealthInfo.batteryHealth != mLastBatteryHealth ||
553 mHealthInfo.batteryPresent != mLastBatteryPresent ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 mPlugType != mLastPlugType) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800555 EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
Yifan Honge41e9392017-11-06 12:56:59 -0800556 mHealthInfo.batteryStatus, mHealthInfo.batteryHealth, mHealthInfo.batteryPresent ? 1 : 0,
557 mPlugType, mHealthInfo.batteryTechnology);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 }
Yifan Honge41e9392017-11-06 12:56:59 -0800559 if (mHealthInfo.batteryLevel != mLastBatteryLevel) {
Dianne Hackborncf1171642013-07-12 17:26:02 -0700560 // Don't do this just from voltage or temperature changes, that is
561 // too noisy.
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800562 EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
Yifan Honge41e9392017-11-06 12:56:59 -0800563 mHealthInfo.batteryLevel, mHealthInfo.batteryVoltage, mHealthInfo.batteryTemperature);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 }
565 if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
566 mPlugType == BATTERY_PLUGGED_NONE) {
567 // We want to make sure we log discharge cycle outliers
568 // if the battery is about to die.
The Android Open Source Project10592532009-03-18 17:39:46 -0700569 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
570 logOutlier = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800572
Dianne Hackborn14272302014-06-10 23:13:02 -0700573 if (!mBatteryLevelLow) {
574 // Should we now switch in to low battery mode?
575 if (mPlugType == BATTERY_PLUGGED_NONE
Todd Poynorb41df4422017-12-08 10:44:40 -0800576 && mHealthInfo.batteryStatus !=
577 BatteryManager.BATTERY_STATUS_UNKNOWN
Yifan Honge41e9392017-11-06 12:56:59 -0800578 && mHealthInfo.batteryLevel <= mLowBatteryWarningLevel) {
Dianne Hackborn14272302014-06-10 23:13:02 -0700579 mBatteryLevelLow = true;
580 }
581 } else {
582 // Should we now switch out of low battery mode?
583 if (mPlugType != BATTERY_PLUGGED_NONE) {
584 mBatteryLevelLow = false;
Yifan Honge41e9392017-11-06 12:56:59 -0800585 } else if (mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) {
Dianne Hackborn14272302014-06-10 23:13:02 -0700586 mBatteryLevelLow = false;
Yifan Honge41e9392017-11-06 12:56:59 -0800587 } else if (force && mHealthInfo.batteryLevel >= mLowBatteryWarningLevel) {
Dianne Hackborn14272302014-06-10 23:13:02 -0700588 // If being forced, the previous state doesn't matter, we will just
589 // absolutely check to see if we are now above the warning level.
590 mBatteryLevelLow = false;
591 }
592 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800593
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800594 mSequence++;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800595
Christopher Tate06ba5542009-04-09 16:03:56 -0700596 // Separate broadcast is sent for power connected / not connected
597 // since the standard intent will not wake any applications and some
598 // applications may want to have smart behavior based on this.
599 if (mPlugType != 0 && mLastPlugType == 0) {
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800600 final Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);
601 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
602 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
Jeff Brown605ea692012-10-05 16:33:10 -0700603 mHandler.post(new Runnable() {
604 @Override
605 public void run() {
Jeff Brown605ea692012-10-05 16:33:10 -0700606 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
607 }
608 });
Christopher Tate06ba5542009-04-09 16:03:56 -0700609 }
610 else if (mPlugType == 0 && mLastPlugType != 0) {
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800611 final Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
612 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
613 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
Jeff Brown605ea692012-10-05 16:33:10 -0700614 mHandler.post(new Runnable() {
615 @Override
616 public void run() {
Jeff Brown605ea692012-10-05 16:33:10 -0700617 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
618 }
619 });
Christopher Tate06ba5542009-04-09 16:03:56 -0700620 }
Mihai Predaa82842f2009-04-29 15:05:56 +0200621
Dianne Hackborn14272302014-06-10 23:13:02 -0700622 if (shouldSendBatteryLowLocked()) {
Dianne Hackborn8ec5b832009-07-01 21:19:35 -0700623 mSentLowBatteryBroadcast = true;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800624 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);
625 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
626 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
Jeff Brown605ea692012-10-05 16:33:10 -0700627 mHandler.post(new Runnable() {
628 @Override
629 public void run() {
Jeff Brown605ea692012-10-05 16:33:10 -0700630 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
631 }
632 });
Dianne Hackborn532ea262017-03-17 17:50:55 -0700633 } else if (mSentLowBatteryBroadcast &&
Yifan Honge41e9392017-11-06 12:56:59 -0800634 mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) {
Dianne Hackborn8ec5b832009-07-01 21:19:35 -0700635 mSentLowBatteryBroadcast = false;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800636 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
637 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
638 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
Jeff Brown605ea692012-10-05 16:33:10 -0700639 mHandler.post(new Runnable() {
640 @Override
641 public void run() {
Jeff Brown605ea692012-10-05 16:33:10 -0700642 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
643 }
644 });
Mihai Predaa82842f2009-04-29 15:05:56 +0200645 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800646
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800647 // We are doing this after sending the above broadcasts, so anything processing
648 // them will get the new sequence number at that point. (See for example how testing
649 // of JobScheduler's BatteryController works.)
Fyodor Kupolov70e75432018-03-01 18:29:06 -0800650 sendBatteryChangedIntentLocked();
Suprabh Shukla8046ba62018-08-08 18:16:22 -0700651 if (mLastBatteryLevel != mHealthInfo.batteryLevel || mLastPlugType != mPlugType) {
Fyodor Kupolov70e75432018-03-01 18:29:06 -0800652 sendBatteryLevelChangedIntentLocked();
653 }
654
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800655
Joe Onoratode1b3592010-10-25 20:36:47 -0700656 // Update the battery LED
657 mLed.updateLightsLocked();
658
The Android Open Source Project10592532009-03-18 17:39:46 -0700659 // This needs to be done after sendIntent() so that we get the lastest battery stats.
660 if (logOutlier && dischargeDuration != 0) {
Jeff Browna4d82042012-10-02 19:11:19 -0700661 logOutlierLocked(dischargeDuration);
The Android Open Source Project10592532009-03-18 17:39:46 -0700662 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800663
Yifan Honge41e9392017-11-06 12:56:59 -0800664 mLastBatteryStatus = mHealthInfo.batteryStatus;
665 mLastBatteryHealth = mHealthInfo.batteryHealth;
666 mLastBatteryPresent = mHealthInfo.batteryPresent;
667 mLastBatteryLevel = mHealthInfo.batteryLevel;
Dianne Hackborn99f7eb452009-09-22 17:27:53 -0700668 mLastPlugType = mPlugType;
Yifan Honge41e9392017-11-06 12:56:59 -0800669 mLastBatteryVoltage = mHealthInfo.batteryVoltage;
670 mLastBatteryTemperature = mHealthInfo.batteryTemperature;
671 mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrent;
672 mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltage;
673 mLastChargeCounter = mHealthInfo.batteryChargeCounter;
Dianne Hackborn99f7eb452009-09-22 17:27:53 -0700674 mLastBatteryLevelCritical = mBatteryLevelCritical;
Mike Lockwooddeff9c82010-09-04 10:29:17 -0400675 mLastInvalidCharger = mInvalidCharger;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 }
677 }
678
Fyodor Kupolov70e75432018-03-01 18:29:06 -0800679 private void sendBatteryChangedIntentLocked() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800680 // Pack up the values and broadcast them to everyone
Jeff Brown605ea692012-10-05 16:33:10 -0700681 final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800682 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
683 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800684
Yifan Honge41e9392017-11-06 12:56:59 -0800685 int icon = getIconLocked(mHealthInfo.batteryLevel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800687 intent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
Yifan Honge41e9392017-11-06 12:56:59 -0800688 intent.putExtra(BatteryManager.EXTRA_STATUS, mHealthInfo.batteryStatus);
689 intent.putExtra(BatteryManager.EXTRA_HEALTH, mHealthInfo.batteryHealth);
690 intent.putExtra(BatteryManager.EXTRA_PRESENT, mHealthInfo.batteryPresent);
691 intent.putExtra(BatteryManager.EXTRA_LEVEL, mHealthInfo.batteryLevel);
Dianne Hackborn74ed6ec2017-11-21 16:33:44 -0800692 intent.putExtra(BatteryManager.EXTRA_BATTERY_LOW, mSentLowBatteryBroadcast);
Dianne Hackbornedd93162009-09-19 14:03:05 -0700693 intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
694 intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
695 intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
Yifan Honge41e9392017-11-06 12:56:59 -0800696 intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage);
697 intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperature);
698 intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mHealthInfo.batteryTechnology);
Mike Lockwooddeff9c82010-09-04 10:29:17 -0400699 intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
Yifan Honge41e9392017-11-06 12:56:59 -0800700 intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent);
701 intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltage);
702 intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
Jeff Browna4d82042012-10-02 19:11:19 -0700703 if (DEBUG) {
Yifan Hong932190b2017-10-11 11:00:51 -0700704 Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. scale:" + BATTERY_SCALE
705 + ", info:" + mHealthInfo.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706 }
707
Fyodor Kupolov70e75432018-03-01 18:29:06 -0800708 mHandler.post(() -> ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL));
709 }
710
711 private void sendBatteryLevelChangedIntentLocked() {
712 Bundle event = new Bundle();
713 long now = SystemClock.elapsedRealtime();
714 event.putInt(BatteryManager.EXTRA_SEQUENCE, mSequence);
715 event.putInt(BatteryManager.EXTRA_STATUS, mHealthInfo.batteryStatus);
716 event.putInt(BatteryManager.EXTRA_HEALTH, mHealthInfo.batteryHealth);
717 event.putBoolean(BatteryManager.EXTRA_PRESENT, mHealthInfo.batteryPresent);
718 event.putInt(BatteryManager.EXTRA_LEVEL, mHealthInfo.batteryLevel);
719 event.putBoolean(BatteryManager.EXTRA_BATTERY_LOW, mSentLowBatteryBroadcast);
720 event.putInt(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
721 event.putInt(BatteryManager.EXTRA_PLUGGED, mPlugType);
722 event.putInt(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage);
Kweku Adamsbad74132019-10-21 12:30:36 -0700723 event.putInt(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperature);
724 event.putInt(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
Fyodor Kupolov70e75432018-03-01 18:29:06 -0800725 event.putLong(BatteryManager.EXTRA_EVENT_TIMESTAMP, now);
726
727 boolean queueWasEmpty = mBatteryLevelsEventQueue.isEmpty();
728 mBatteryLevelsEventQueue.add(event);
729 // Make sure queue is bounded and doesn't exceed intent payload limits
730 if (mBatteryLevelsEventQueue.size() > MAX_BATTERY_LEVELS_QUEUE_SIZE) {
731 mBatteryLevelsEventQueue.removeFirst();
732 }
733
734 if (queueWasEmpty) {
735 // send now if last event was before throttle interval, otherwise delay
736 long delay = now - mLastBatteryLevelChangedSentMs > BATTERY_LEVEL_CHANGE_THROTTLE_MS
737 ? 0 : mLastBatteryLevelChangedSentMs + BATTERY_LEVEL_CHANGE_THROTTLE_MS - now;
738 mHandler.postDelayed(this::sendEnqueuedBatteryLevelChangedEvents, delay);
739 }
740 }
741
742 private void sendEnqueuedBatteryLevelChangedEvents() {
743 ArrayList<Bundle> events;
744 synchronized (mLock) {
745 events = new ArrayList<>(mBatteryLevelsEventQueue);
746 mBatteryLevelsEventQueue.clear();
747 }
748 final Intent intent = new Intent(Intent.ACTION_BATTERY_LEVEL_CHANGED);
749 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
750 intent.putParcelableArrayListExtra(BatteryManager.EXTRA_EVENTS, events);
751
752 mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
753 android.Manifest.permission.BATTERY_STATS);
754 mLastBatteryLevelChangedSentMs = SystemClock.elapsedRealtime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800755 }
756
Mike Ma99e57c32018-03-15 14:40:37 -0700757 // TODO: Current code doesn't work since "--unplugged" flag in BSS was purposefully removed.
Jeff Browna4d82042012-10-02 19:11:19 -0700758 private void logBatteryStatsLocked() {
Dianne Hackborn8c841092013-06-24 13:46:13 -0700759 IBinder batteryInfoService = ServiceManager.getService(BatteryStats.SERVICE_NAME);
Dan Egnor18e93962010-02-10 19:27:58 -0800760 if (batteryInfoService == null) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761
Dan Egnor18e93962010-02-10 19:27:58 -0800762 DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
763 if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return;
764
765 File dumpFile = null;
766 FileOutputStream dumpStream = null;
767 try {
768 // dump the service to a file
Dianne Hackborn8c841092013-06-24 13:46:13 -0700769 dumpFile = new File(DUMPSYS_DATA_PATH + BatteryStats.SERVICE_NAME + ".dump");
Dan Egnor18e93962010-02-10 19:27:58 -0800770 dumpStream = new FileOutputStream(dumpFile);
771 batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);
Dianne Hackborn8bdf5932010-10-15 12:54:40 -0700772 FileUtils.sync(dumpStream);
Dan Egnor18e93962010-02-10 19:27:58 -0800773
774 // add dump file to drop box
775 db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT);
776 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800777 Slog.e(TAG, "failed to dump battery service", e);
Dan Egnor18e93962010-02-10 19:27:58 -0800778 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800779 Slog.e(TAG, "failed to write dumpsys file", e);
Dan Egnor18e93962010-02-10 19:27:58 -0800780 } finally {
781 // make sure we clean up
782 if (dumpStream != null) {
783 try {
784 dumpStream.close();
785 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800786 Slog.e(TAG, "failed to close dumpsys output stream");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800787 }
Dan Egnor18e93962010-02-10 19:27:58 -0800788 }
789 if (dumpFile != null && !dumpFile.delete()) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800790 Slog.e(TAG, "failed to delete temporary dumpsys file: "
Dan Egnor18e93962010-02-10 19:27:58 -0800791 + dumpFile.getAbsolutePath());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792 }
793 }
794 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800795
Jeff Browna4d82042012-10-02 19:11:19 -0700796 private void logOutlierLocked(long duration) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800797 ContentResolver cr = mContext.getContentResolver();
Jeff Sharkey625239a2012-09-26 22:03:49 -0700798 String dischargeThresholdString = Settings.Global.getString(cr,
799 Settings.Global.BATTERY_DISCHARGE_THRESHOLD);
800 String durationThresholdString = Settings.Global.getString(cr,
801 Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800802
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 if (dischargeThresholdString != null && durationThresholdString != null) {
804 try {
805 long durationThreshold = Long.parseLong(durationThresholdString);
806 int dischargeThreshold = Integer.parseInt(dischargeThresholdString);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800807 if (duration <= durationThreshold &&
Yifan Honge41e9392017-11-06 12:56:59 -0800808 mDischargeStartLevel - mHealthInfo.batteryLevel >= dischargeThreshold) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 // If the discharge cycle is bad enough we want to know about it.
Jeff Browna4d82042012-10-02 19:11:19 -0700810 logBatteryStatsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800811 }
Jeff Browna4d82042012-10-02 19:11:19 -0700812 if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 " discharge threshold: " + dischargeThreshold);
Jeff Browna4d82042012-10-02 19:11:19 -0700814 if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +
Yifan Honge41e9392017-11-06 12:56:59 -0800815 (mDischargeStartLevel - mHealthInfo.batteryLevel));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800816 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800817 Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 durationThresholdString + " or " + dischargeThresholdString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800819 }
820 }
821 }
822
Jeff Browna4d82042012-10-02 19:11:19 -0700823 private int getIconLocked(int level) {
Yifan Honge41e9392017-11-06 12:56:59 -0800824 if (mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 return com.android.internal.R.drawable.stat_sys_battery_charge;
Yifan Honge41e9392017-11-06 12:56:59 -0800826 } else if (mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827 return com.android.internal.R.drawable.stat_sys_battery;
Yifan Honge41e9392017-11-06 12:56:59 -0800828 } else if (mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
829 || mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
Jeff Browna4d82042012-10-02 19:11:19 -0700830 if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)
Yifan Honge41e9392017-11-06 12:56:59 -0800831 && mHealthInfo.batteryLevel >= 100) {
Joe Onorato794be402010-11-21 19:22:25 -0800832 return com.android.internal.R.drawable.stat_sys_battery_charge;
833 } else {
834 return com.android.internal.R.drawable.stat_sys_battery;
835 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836 } else {
837 return com.android.internal.R.drawable.stat_sys_battery_unknown;
838 }
839 }
840
Dianne Hackborn2e441072015-10-28 18:00:57 -0700841 class Shell extends ShellCommand {
842 @Override
843 public int onCommand(String cmd) {
844 return onShellCommand(this, cmd);
845 }
846
847 @Override
848 public void onHelp() {
849 PrintWriter pw = getOutPrintWriter();
850 dumpHelp(pw);
851 }
852 }
853
854 static void dumpHelp(PrintWriter pw) {
855 pw.println("Battery service (battery) commands:");
856 pw.println(" help");
857 pw.println(" Print this help text.");
Adam Lesinski29ddfe52017-03-29 19:29:00 -0700858 pw.println(" set [-f] [ac|usb|wireless|status|level|temp|present|invalid] <value>");
Dianne Hackborn2e441072015-10-28 18:00:57 -0700859 pw.println(" Force a battery property value, freezing battery state.");
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800860 pw.println(" -f: force a battery change broadcast be sent, prints new sequence.");
861 pw.println(" unplug [-f]");
Dianne Hackborn2e441072015-10-28 18:00:57 -0700862 pw.println(" Force battery unplugged, freezing battery state.");
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800863 pw.println(" -f: force a battery change broadcast be sent, prints new sequence.");
864 pw.println(" reset [-f]");
Dianne Hackborn2e441072015-10-28 18:00:57 -0700865 pw.println(" Unfreeze battery state, returning to current hardware values.");
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800866 pw.println(" -f: force a battery change broadcast be sent, prints new sequence.");
867 }
868
869 static final int OPTION_FORCE_UPDATE = 1<<0;
870
871 int parseOptions(Shell shell) {
872 String opt;
873 int opts = 0;
874 while ((opt = shell.getNextOption()) != null) {
875 if ("-f".equals(opt)) {
876 opts |= OPTION_FORCE_UPDATE;
877 }
878 }
879 return opts;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700880 }
881
882 int onShellCommand(Shell shell, String cmd) {
883 if (cmd == null) {
884 return shell.handleDefaultCommands(cmd);
885 }
886 PrintWriter pw = shell.getOutPrintWriter();
887 switch (cmd) {
888 case "unplug": {
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800889 int opts = parseOptions(shell);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700890 getContext().enforceCallingOrSelfPermission(
891 android.Manifest.permission.DEVICE_POWER, null);
892 if (!mUpdatesStopped) {
Yifan Hong932190b2017-10-11 11:00:51 -0700893 copy(mLastHealthInfo, mHealthInfo);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700894 }
Yifan Honge41e9392017-11-06 12:56:59 -0800895 mHealthInfo.chargerAcOnline = false;
896 mHealthInfo.chargerUsbOnline = false;
897 mHealthInfo.chargerWirelessOnline = false;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700898 long ident = Binder.clearCallingIdentity();
899 try {
900 mUpdatesStopped = true;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800901 processValuesFromShellLocked(pw, opts);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700902 } finally {
903 Binder.restoreCallingIdentity(ident);
904 }
905 } break;
906 case "set": {
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800907 int opts = parseOptions(shell);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700908 getContext().enforceCallingOrSelfPermission(
909 android.Manifest.permission.DEVICE_POWER, null);
910 final String key = shell.getNextArg();
911 if (key == null) {
912 pw.println("No property specified");
913 return -1;
914
915 }
916 final String value = shell.getNextArg();
917 if (value == null) {
918 pw.println("No value specified");
919 return -1;
920
921 }
922 try {
923 if (!mUpdatesStopped) {
Yifan Hong932190b2017-10-11 11:00:51 -0700924 copy(mLastHealthInfo, mHealthInfo);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700925 }
926 boolean update = true;
927 switch (key) {
Christopher Tate630d98b2017-03-07 14:12:26 -0800928 case "present":
Yifan Honge41e9392017-11-06 12:56:59 -0800929 mHealthInfo.batteryPresent = Integer.parseInt(value) != 0;
Christopher Tate630d98b2017-03-07 14:12:26 -0800930 break;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700931 case "ac":
Yifan Honge41e9392017-11-06 12:56:59 -0800932 mHealthInfo.chargerAcOnline = Integer.parseInt(value) != 0;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700933 break;
934 case "usb":
Yifan Honge41e9392017-11-06 12:56:59 -0800935 mHealthInfo.chargerUsbOnline = Integer.parseInt(value) != 0;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700936 break;
937 case "wireless":
Yifan Honge41e9392017-11-06 12:56:59 -0800938 mHealthInfo.chargerWirelessOnline = Integer.parseInt(value) != 0;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700939 break;
940 case "status":
Yifan Honge41e9392017-11-06 12:56:59 -0800941 mHealthInfo.batteryStatus = Integer.parseInt(value);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700942 break;
943 case "level":
Yifan Honge41e9392017-11-06 12:56:59 -0800944 mHealthInfo.batteryLevel = Integer.parseInt(value);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700945 break;
Makoto Onuki076218b2018-01-26 10:26:36 -0800946 case "counter":
947 mHealthInfo.batteryChargeCounter = Integer.parseInt(value);
948 break;
Adam Lesinski29ddfe52017-03-29 19:29:00 -0700949 case "temp":
Yifan Honge41e9392017-11-06 12:56:59 -0800950 mHealthInfo.batteryTemperature = Integer.parseInt(value);
Adam Lesinski29ddfe52017-03-29 19:29:00 -0700951 break;
Dianne Hackborn2e441072015-10-28 18:00:57 -0700952 case "invalid":
953 mInvalidCharger = Integer.parseInt(value);
954 break;
955 default:
956 pw.println("Unknown set option: " + key);
957 update = false;
958 break;
959 }
960 if (update) {
961 long ident = Binder.clearCallingIdentity();
962 try {
963 mUpdatesStopped = true;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800964 processValuesFromShellLocked(pw, opts);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700965 } finally {
966 Binder.restoreCallingIdentity(ident);
967 }
968 }
969 } catch (NumberFormatException ex) {
970 pw.println("Bad value: " + value);
971 return -1;
972 }
973 } break;
974 case "reset": {
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800975 int opts = parseOptions(shell);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700976 getContext().enforceCallingOrSelfPermission(
977 android.Manifest.permission.DEVICE_POWER, null);
978 long ident = Binder.clearCallingIdentity();
979 try {
980 if (mUpdatesStopped) {
981 mUpdatesStopped = false;
Yifan Hong932190b2017-10-11 11:00:51 -0700982 copy(mHealthInfo, mLastHealthInfo);
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800983 processValuesFromShellLocked(pw, opts);
Dianne Hackborn2e441072015-10-28 18:00:57 -0700984 }
985 } finally {
986 Binder.restoreCallingIdentity(ident);
987 }
988 } break;
989 default:
990 return shell.handleDefaultCommands(cmd);
991 }
992 return 0;
993 }
994
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800995 private void processValuesFromShellLocked(PrintWriter pw, int opts) {
996 processValuesLocked((opts & OPTION_FORCE_UPDATE) != 0);
997 if ((opts & OPTION_FORCE_UPDATE) != 0) {
998 pw.println(mSequence);
999 }
1000 }
1001
Dianne Hackborn2e441072015-10-28 18:00:57 -07001002 private void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Browna4d82042012-10-02 19:11:19 -07001003 synchronized (mLock) {
1004 if (args == null || args.length == 0 || "-a".equals(args[0])) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -07001005 pw.println("Current Battery Service state:");
Dianne Hackbornc428aae2012-10-03 16:38:22 -07001006 if (mUpdatesStopped) {
1007 pw.println(" (UPDATES STOPPED -- use 'reset' to restart)");
1008 }
Yifan Honge41e9392017-11-06 12:56:59 -08001009 pw.println(" AC powered: " + mHealthInfo.chargerAcOnline);
1010 pw.println(" USB powered: " + mHealthInfo.chargerUsbOnline);
1011 pw.println(" Wireless powered: " + mHealthInfo.chargerWirelessOnline);
1012 pw.println(" Max charging current: " + mHealthInfo.maxChargingCurrent);
1013 pw.println(" Max charging voltage: " + mHealthInfo.maxChargingVoltage);
1014 pw.println(" Charge counter: " + mHealthInfo.batteryChargeCounter);
1015 pw.println(" status: " + mHealthInfo.batteryStatus);
1016 pw.println(" health: " + mHealthInfo.batteryHealth);
1017 pw.println(" present: " + mHealthInfo.batteryPresent);
1018 pw.println(" level: " + mHealthInfo.batteryLevel);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -07001019 pw.println(" scale: " + BATTERY_SCALE);
Yifan Honge41e9392017-11-06 12:56:59 -08001020 pw.println(" voltage: " + mHealthInfo.batteryVoltage);
1021 pw.println(" temperature: " + mHealthInfo.batteryTemperature);
1022 pw.println(" technology: " + mHealthInfo.batteryTechnology);
Dianne Hackbornc428aae2012-10-03 16:38:22 -07001023 } else {
Dianne Hackborn2e441072015-10-28 18:00:57 -07001024 Shell shell = new Shell();
Dianne Hackborn354736e2016-08-22 17:00:05 -07001025 shell.exec(mBinderService, null, fd, null, args, null, new ResultReceiver(null));
Joe Onorato4ca7f1e2010-10-27 15:32:23 -07001026 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001027 }
1028 }
Joe Onoratode1b3592010-10-25 20:36:47 -07001029
Netta Pe2a3cd82017-01-26 18:03:51 -08001030 private void dumpProto(FileDescriptor fd) {
1031 final ProtoOutputStream proto = new ProtoOutputStream(fd);
1032
1033 synchronized (mLock) {
1034 proto.write(BatteryServiceDumpProto.ARE_UPDATES_STOPPED, mUpdatesStopped);
Bookatz1a1b0462018-01-12 11:47:03 -08001035 int batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_NONE;
Yifan Honge41e9392017-11-06 12:56:59 -08001036 if (mHealthInfo.chargerAcOnline) {
Bookatz1a1b0462018-01-12 11:47:03 -08001037 batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_AC;
Yifan Honge41e9392017-11-06 12:56:59 -08001038 } else if (mHealthInfo.chargerUsbOnline) {
Bookatz1a1b0462018-01-12 11:47:03 -08001039 batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_USB;
Yifan Honge41e9392017-11-06 12:56:59 -08001040 } else if (mHealthInfo.chargerWirelessOnline) {
Bookatz1a1b0462018-01-12 11:47:03 -08001041 batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_WIRELESS;
Netta Pe2a3cd82017-01-26 18:03:51 -08001042 }
1043 proto.write(BatteryServiceDumpProto.PLUGGED, batteryPluggedValue);
Yifan Honge41e9392017-11-06 12:56:59 -08001044 proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent);
1045 proto.write(BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltage);
1046 proto.write(BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
1047 proto.write(BatteryServiceDumpProto.STATUS, mHealthInfo.batteryStatus);
1048 proto.write(BatteryServiceDumpProto.HEALTH, mHealthInfo.batteryHealth);
1049 proto.write(BatteryServiceDumpProto.IS_PRESENT, mHealthInfo.batteryPresent);
1050 proto.write(BatteryServiceDumpProto.LEVEL, mHealthInfo.batteryLevel);
Netta Pe2a3cd82017-01-26 18:03:51 -08001051 proto.write(BatteryServiceDumpProto.SCALE, BATTERY_SCALE);
Yifan Honge41e9392017-11-06 12:56:59 -08001052 proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.batteryVoltage);
1053 proto.write(BatteryServiceDumpProto.TEMPERATURE, mHealthInfo.batteryTemperature);
1054 proto.write(BatteryServiceDumpProto.TECHNOLOGY, mHealthInfo.batteryTechnology);
Netta Pe2a3cd82017-01-26 18:03:51 -08001055 }
1056 proto.flush();
1057 }
1058
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001059 private static void traceBegin(String name) {
1060 Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
1061 }
1062
1063 private static void traceEnd() {
1064 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
1065 }
1066
Jeff Browna4d82042012-10-02 19:11:19 -07001067 private final class Led {
Adam Lesinskief2ea1f2013-12-05 16:48:06 -08001068 private final Light mBatteryLight;
Joe Onoratode1b3592010-10-25 20:36:47 -07001069
Jeff Browna4d82042012-10-02 19:11:19 -07001070 private final int mBatteryLowARGB;
1071 private final int mBatteryMediumARGB;
1072 private final int mBatteryFullARGB;
1073 private final int mBatteryLedOn;
1074 private final int mBatteryLedOff;
1075
Adam Lesinskief2ea1f2013-12-05 16:48:06 -08001076 public Led(Context context, LightsManager lights) {
1077 mBatteryLight = lights.getLight(LightsManager.LIGHT_ID_BATTERY);
Joe Onoratode1b3592010-10-25 20:36:47 -07001078
Jeff Browna4d82042012-10-02 19:11:19 -07001079 mBatteryLowARGB = context.getResources().getInteger(
Joe Onoratode1b3592010-10-25 20:36:47 -07001080 com.android.internal.R.integer.config_notificationsBatteryLowARGB);
Jeff Browna4d82042012-10-02 19:11:19 -07001081 mBatteryMediumARGB = context.getResources().getInteger(
Joe Onoratode1b3592010-10-25 20:36:47 -07001082 com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
Jeff Browna4d82042012-10-02 19:11:19 -07001083 mBatteryFullARGB = context.getResources().getInteger(
Joe Onoratode1b3592010-10-25 20:36:47 -07001084 com.android.internal.R.integer.config_notificationsBatteryFullARGB);
Jeff Browna4d82042012-10-02 19:11:19 -07001085 mBatteryLedOn = context.getResources().getInteger(
Joe Onoratode1b3592010-10-25 20:36:47 -07001086 com.android.internal.R.integer.config_notificationsBatteryLedOn);
Jeff Browna4d82042012-10-02 19:11:19 -07001087 mBatteryLedOff = context.getResources().getInteger(
Joe Onoratode1b3592010-10-25 20:36:47 -07001088 com.android.internal.R.integer.config_notificationsBatteryLedOff);
1089 }
1090
1091 /**
1092 * Synchronize on BatteryService.
1093 */
Jeff Browna4d82042012-10-02 19:11:19 -07001094 public void updateLightsLocked() {
Yifan Honge41e9392017-11-06 12:56:59 -08001095 final int level = mHealthInfo.batteryLevel;
1096 final int status = mHealthInfo.batteryStatus;
Joe Onoratode1b3592010-10-25 20:36:47 -07001097 if (level < mLowBatteryWarningLevel) {
1098 if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
1099 // Solid red when battery is charging
1100 mBatteryLight.setColor(mBatteryLowARGB);
1101 } else {
1102 // Flash red when battery is low and not charging
Adam Lesinskief2ea1f2013-12-05 16:48:06 -08001103 mBatteryLight.setFlashing(mBatteryLowARGB, Light.LIGHT_FLASH_TIMED,
Joe Onoratode1b3592010-10-25 20:36:47 -07001104 mBatteryLedOn, mBatteryLedOff);
1105 }
1106 } else if (status == BatteryManager.BATTERY_STATUS_CHARGING
1107 || status == BatteryManager.BATTERY_STATUS_FULL) {
1108 if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
1109 // Solid green when full or charging and nearly full
1110 mBatteryLight.setColor(mBatteryFullARGB);
1111 } else {
1112 // Solid orange when charging and halfway full
1113 mBatteryLight.setColor(mBatteryMediumARGB);
1114 }
1115 } else {
1116 // No lights if not charging and not low
1117 mBatteryLight.turnOff();
1118 }
1119 }
1120 }
Todd Poynor26faecc2013-05-22 18:54:48 -07001121
Yifan Hong89d55c12017-10-11 11:29:01 -07001122 private final class HealthHalCallback extends IHealthInfoCallback.Stub
1123 implements HealthServiceWrapper.Callback {
Hridya Valsarajua09fa902018-01-17 23:04:46 -08001124 @Override public void healthInfoChanged(android.hardware.health.V2_0.HealthInfo props) {
Yifan Hong89d55c12017-10-11 11:29:01 -07001125 BatteryService.this.update(props);
1126 }
1127 // on new service registered
1128 @Override public void onRegistration(IHealth oldService, IHealth newService,
1129 String instance) {
1130 if (newService == null) return;
1131
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001132 traceBegin("HealthUnregisterCallback");
Adam Lesinskief2ea1f2013-12-05 16:48:06 -08001133 try {
Yifan Hong89d55c12017-10-11 11:29:01 -07001134 if (oldService != null) {
1135 int r = oldService.unregisterCallback(this);
1136 if (r != Result.SUCCESS) {
1137 Slog.w(TAG, "health: cannot unregister previous callback: " +
1138 Result.toString(r));
1139 }
1140 }
1141 } catch (RemoteException ex) {
1142 Slog.w(TAG, "health: cannot unregister previous callback (transaction error): "
1143 + ex.getMessage());
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001144 } finally {
1145 traceEnd();
Adam Lesinskief2ea1f2013-12-05 16:48:06 -08001146 }
Yifan Hong89d55c12017-10-11 11:29:01 -07001147
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001148 traceBegin("HealthRegisterCallback");
Yifan Hong89d55c12017-10-11 11:29:01 -07001149 try {
1150 int r = newService.registerCallback(this);
1151 if (r != Result.SUCCESS) {
1152 Slog.w(TAG, "health: cannot register callback: " + Result.toString(r));
1153 return;
1154 }
1155 // registerCallback does NOT guarantee that update is called
1156 // immediately, so request a manual update here.
1157 newService.update();
1158 } catch (RemoteException ex) {
1159 Slog.e(TAG, "health: cannot register callback (transaction error): "
1160 + ex.getMessage());
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001161 } finally {
1162 traceEnd();
Yifan Hong89d55c12017-10-11 11:29:01 -07001163 }
1164 }
Todd Poynor26faecc2013-05-22 18:54:48 -07001165 }
Jeff Brown21392762014-06-13 19:00:36 -07001166
1167 private final class BinderService extends Binder {
Dianne Hackborn2e441072015-10-28 18:00:57 -07001168 @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -06001169 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
Jeff Brown21392762014-06-13 19:00:36 -07001170
Netta Pe2a3cd82017-01-26 18:03:51 -08001171 if (args.length > 0 && "--proto".equals(args[0])) {
1172 dumpProto(fd);
1173 } else {
1174 dumpInternal(fd, pw, args);
1175 }
Dianne Hackborn2e441072015-10-28 18:00:57 -07001176 }
1177
1178 @Override public void onShellCommand(FileDescriptor in, FileDescriptor out,
Dianne Hackborn354736e2016-08-22 17:00:05 -07001179 FileDescriptor err, String[] args, ShellCallback callback,
1180 ResultReceiver resultReceiver) {
1181 (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
Jeff Brown21392762014-06-13 19:00:36 -07001182 }
1183 }
1184
Yifan Hong1fd86f4c2017-10-09 16:50:33 -07001185 // Reduced IBatteryPropertiesRegistrar that only implements getProperty for usage
1186 // in BatteryManager.
1187 private final class BatteryPropertiesRegistrar extends IBatteryPropertiesRegistrar.Stub {
Yifan Hong58faaa02018-05-04 13:48:31 -07001188 @Override
Yifan Hong1fd86f4c2017-10-09 16:50:33 -07001189 public int getProperty(int id, final BatteryProperty prop) throws RemoteException {
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001190 traceBegin("HealthGetProperty");
1191 try {
1192 IHealth service = mHealthServiceWrapper.getLastService();
1193 if (service == null) throw new RemoteException("no health service");
1194 final MutableInt outResult = new MutableInt(Result.NOT_SUPPORTED);
1195 switch(id) {
1196 case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER:
1197 service.getChargeCounter((int result, int value) -> {
1198 outResult.value = result;
1199 if (result == Result.SUCCESS) prop.setLong(value);
1200 });
1201 break;
1202 case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW:
1203 service.getCurrentNow((int result, int value) -> {
1204 outResult.value = result;
1205 if (result == Result.SUCCESS) prop.setLong(value);
1206 });
1207 break;
1208 case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE:
1209 service.getCurrentAverage((int result, int value) -> {
1210 outResult.value = result;
1211 if (result == Result.SUCCESS) prop.setLong(value);
1212 });
1213 break;
1214 case BatteryManager.BATTERY_PROPERTY_CAPACITY:
1215 service.getCapacity((int result, int value) -> {
1216 outResult.value = result;
1217 if (result == Result.SUCCESS) prop.setLong(value);
1218 });
1219 break;
1220 case BatteryManager.BATTERY_PROPERTY_STATUS:
1221 service.getChargeStatus((int result, int value) -> {
1222 outResult.value = result;
1223 if (result == Result.SUCCESS) prop.setLong(value);
1224 });
1225 break;
1226 case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER:
1227 service.getEnergyCounter((int result, long value) -> {
1228 outResult.value = result;
1229 if (result == Result.SUCCESS) prop.setLong(value);
1230 });
1231 break;
1232 }
1233 return outResult.value;
1234 } finally {
1235 traceEnd();
Yifan Hong1fd86f4c2017-10-09 16:50:33 -07001236 }
Yifan Hong1fd86f4c2017-10-09 16:50:33 -07001237 }
Yifan Hong58faaa02018-05-04 13:48:31 -07001238 @Override
Yifan Hong7838fcb2017-10-24 16:06:27 -07001239 public void scheduleUpdate() throws RemoteException {
Yifan Hong7ceb7242019-08-16 13:20:57 -07001240 mHealthServiceWrapper.getHandlerThread().getThreadHandler().post(() -> {
1241 traceBegin("HealthScheduleUpdate");
1242 try {
1243 IHealth service = mHealthServiceWrapper.getLastService();
1244 if (service == null) {
1245 Slog.e(TAG, "no health service");
1246 return;
1247 }
1248 service.update();
1249 } catch (RemoteException ex) {
1250 Slog.e(TAG, "Cannot call update on health HAL", ex);
1251 } finally {
1252 traceEnd();
1253 }
1254 });
Yifan Hong1fd86f4c2017-10-09 16:50:33 -07001255 }
1256 }
1257
Jeff Brown21392762014-06-13 19:00:36 -07001258 private final class LocalService extends BatteryManagerInternal {
1259 @Override
1260 public boolean isPowered(int plugTypeSet) {
1261 synchronized (mLock) {
1262 return isPoweredLocked(plugTypeSet);
1263 }
1264 }
1265
1266 @Override
1267 public int getPlugType() {
1268 synchronized (mLock) {
1269 return mPlugType;
1270 }
1271 }
1272
1273 @Override
1274 public int getBatteryLevel() {
1275 synchronized (mLock) {
Yifan Honge41e9392017-11-06 12:56:59 -08001276 return mHealthInfo.batteryLevel;
Jeff Brown21392762014-06-13 19:00:36 -07001277 }
1278 }
1279
1280 @Override
Makoto Onuki076218b2018-01-26 10:26:36 -08001281 public int getBatteryChargeCounter() {
1282 synchronized (mLock) {
1283 return mHealthInfo.batteryChargeCounter;
1284 }
1285 }
1286
1287 @Override
1288 public int getBatteryFullCharge() {
1289 synchronized (mLock) {
1290 return mHealthInfo.batteryFullCharge;
1291 }
1292 }
1293
1294 @Override
Jeff Brown21392762014-06-13 19:00:36 -07001295 public boolean getBatteryLevelLow() {
1296 synchronized (mLock) {
1297 return mBatteryLevelLow;
1298 }
1299 }
1300
1301 @Override
1302 public int getInvalidCharger() {
1303 synchronized (mLock) {
1304 return mInvalidCharger;
1305 }
1306 }
1307 }
Yifan Hong98852792017-10-12 11:35:14 -07001308
1309 /**
1310 * HealthServiceWrapper wraps the internal IHealth service and refreshes the service when
1311 * necessary.
1312 *
1313 * On new registration of IHealth service, {@link #onRegistration onRegistration} is called and
1314 * the internal service is refreshed.
1315 * On death of an existing IHealth service, the internal service is NOT cleared to avoid
1316 * race condition between death notification and new service notification. Hence,
1317 * a caller must check for transaction errors when calling into the service.
1318 *
1319 * @hide Should only be used internally.
1320 */
1321 @VisibleForTesting
1322 static final class HealthServiceWrapper {
1323 private static final String TAG = "HealthServiceWrapper";
1324 public static final String INSTANCE_HEALTHD = "backup";
1325 public static final String INSTANCE_VENDOR = "default";
1326 // All interesting instances, sorted by priority high -> low.
1327 private static final List<String> sAllInstances =
1328 Arrays.asList(INSTANCE_VENDOR, INSTANCE_HEALTHD);
1329
1330 private final IServiceNotification mNotification = new Notification();
Yifan Hong7ceb7242019-08-16 13:20:57 -07001331 private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceHwbinder");
Yifan Hong1fff9842017-10-27 13:53:10 -07001332 // These variables are fixed after init.
Yifan Hong98852792017-10-12 11:35:14 -07001333 private Callback mCallback;
1334 private IHealthSupplier mHealthSupplier;
Yifan Hong1fff9842017-10-27 13:53:10 -07001335 private String mInstanceName;
Yifan Hong98852792017-10-12 11:35:14 -07001336
Yifan Hong89d55c12017-10-11 11:29:01 -07001337 // Last IHealth service received.
Yifan Hong89d55c12017-10-11 11:29:01 -07001338 private final AtomicReference<IHealth> mLastService = new AtomicReference<>();
1339
Yifan Hong98852792017-10-12 11:35:14 -07001340 /**
1341 * init should be called after constructor. For testing purposes, init is not called by
1342 * constructor.
1343 */
1344 HealthServiceWrapper() {
1345 }
1346
Yifan Hong1fd86f4c2017-10-09 16:50:33 -07001347 IHealth getLastService() {
1348 return mLastService.get();
1349 }
1350
Yifan Hong98852792017-10-12 11:35:14 -07001351 /**
1352 * Start monitoring registration of new IHealth services. Only instances that are in
1353 * {@code sAllInstances} and in device / framework manifest are used. This function should
1354 * only be called once.
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001355 *
1356 * mCallback.onRegistration() is called synchronously (aka in init thread) before
1357 * this method returns.
1358 *
Yifan Hong98852792017-10-12 11:35:14 -07001359 * @throws RemoteException transaction error when talking to IServiceManager
1360 * @throws NoSuchElementException if one of the following cases:
1361 * - No service manager;
1362 * - none of {@code sAllInstances} are in manifests (i.e. not
1363 * available on this device), or none of these instances are available to current
1364 * process.
1365 * @throws NullPointerException when callback is null or supplier is null
1366 */
1367 void init(Callback callback,
1368 IServiceManagerSupplier managerSupplier,
1369 IHealthSupplier healthSupplier)
1370 throws RemoteException, NoSuchElementException, NullPointerException {
1371 if (callback == null || managerSupplier == null || healthSupplier == null)
1372 throw new NullPointerException();
1373
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001374 IServiceManager manager;
1375
Yifan Hong98852792017-10-12 11:35:14 -07001376 mCallback = callback;
1377 mHealthSupplier = healthSupplier;
1378
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001379 // Initialize mLastService and call callback for the first time (in init thread)
1380 IHealth newService = null;
Yifan Hong98852792017-10-12 11:35:14 -07001381 for (String name : sAllInstances) {
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001382 traceBegin("HealthInitGetService_" + name);
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001383 try {
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001384 newService = healthSupplier.get(name);
1385 } catch (NoSuchElementException ex) {
1386 /* ignored, handled below */
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001387 } finally {
1388 traceEnd();
Yifan Hong98852792017-10-12 11:35:14 -07001389 }
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001390 if (newService != null) {
1391 mInstanceName = name;
1392 mLastService.set(newService);
1393 break;
1394 }
Yifan Hong98852792017-10-12 11:35:14 -07001395 }
1396
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001397 if (mInstanceName == null || newService == null) {
Yifan Hong1fff9842017-10-27 13:53:10 -07001398 throw new NoSuchElementException(String.format(
1399 "No IHealth service instance among %s is available. Perhaps no permission?",
1400 sAllInstances.toString()));
1401 }
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001402 mCallback.onRegistration(null, newService, mInstanceName);
Yifan Hong1fff9842017-10-27 13:53:10 -07001403
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001404 // Register for future service registrations
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001405 traceBegin("HealthInitRegisterNotification");
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001406 mHandlerThread.start();
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001407 try {
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001408 managerSupplier.get().registerForNotifications(
1409 IHealth.kInterfaceName, mInstanceName, mNotification);
Yifan Hong8cc18ef2017-10-31 12:27:47 -07001410 } finally {
1411 traceEnd();
1412 }
Yifan Hong1fff9842017-10-27 13:53:10 -07001413 Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + mInstanceName);
Yifan Hong98852792017-10-12 11:35:14 -07001414 }
1415
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001416 @VisibleForTesting
1417 HandlerThread getHandlerThread() {
1418 return mHandlerThread;
1419 }
1420
Yifan Hong98852792017-10-12 11:35:14 -07001421 interface Callback {
1422 /**
1423 * This function is invoked asynchronously when a new and related IServiceNotification
1424 * is received.
1425 * @param service the recently retrieved service from IServiceManager.
1426 * Can be a dead service before service notification of a new service is delivered.
1427 * Implementation must handle cases for {@link RemoteException}s when calling
1428 * into service.
1429 * @param instance instance name.
1430 */
Yifan Hong89d55c12017-10-11 11:29:01 -07001431 void onRegistration(IHealth oldService, IHealth newService, String instance);
Yifan Hong98852792017-10-12 11:35:14 -07001432 }
1433
1434 /**
1435 * Supplier of services.
1436 * Must not return null; throw {@link NoSuchElementException} if a service is not available.
1437 */
1438 interface IServiceManagerSupplier {
Yifan Hong89d55c12017-10-11 11:29:01 -07001439 default IServiceManager get() throws NoSuchElementException, RemoteException {
1440 return IServiceManager.getService();
1441 }
Yifan Hong98852792017-10-12 11:35:14 -07001442 }
1443 /**
1444 * Supplier of services.
1445 * Must not return null; throw {@link NoSuchElementException} if a service is not available.
1446 */
1447 interface IHealthSupplier {
Yifan Hong89d55c12017-10-11 11:29:01 -07001448 default IHealth get(String name) throws NoSuchElementException, RemoteException {
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001449 return IHealth.getService(name, true /* retry */);
Yifan Hong89d55c12017-10-11 11:29:01 -07001450 }
Yifan Hong98852792017-10-12 11:35:14 -07001451 }
1452
1453 private class Notification extends IServiceNotification.Stub {
1454 @Override
1455 public final void onRegistration(String interfaceName, String instanceName,
1456 boolean preexisting) {
1457 if (!IHealth.kInterfaceName.equals(interfaceName)) return;
Yifan Hong1fff9842017-10-27 13:53:10 -07001458 if (!mInstanceName.equals(instanceName)) return;
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001459
1460 // This runnable only runs on mHandlerThread and ordering is ensured, hence
1461 // no locking is needed inside the runnable.
1462 mHandlerThread.getThreadHandler().post(new Runnable() {
1463 @Override
1464 public void run() {
1465 try {
1466 IHealth newService = mHealthSupplier.get(mInstanceName);
1467 IHealth oldService = mLastService.getAndSet(newService);
1468
1469 // preexisting may be inaccurate (race). Check for equality here.
1470 if (Objects.equals(newService, oldService)) return;
1471
1472 Slog.i(TAG, "health: new instance registered " + mInstanceName);
1473 mCallback.onRegistration(oldService, newService, mInstanceName);
1474 } catch (NoSuchElementException | RemoteException ex) {
1475 Slog.e(TAG, "health: Cannot get instance '" + mInstanceName
1476 + "': " + ex.getMessage() + ". Perhaps no permission?");
1477 }
Yifan Hong89d55c12017-10-11 11:29:01 -07001478 }
Yifan Hongcf9a8b22017-11-02 18:43:06 -07001479 });
Yifan Hong98852792017-10-12 11:35:14 -07001480 }
1481 }
1482 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001483}