1 /*
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 
17 package com.android.server;
18 
19 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
20 
21 import android.app.ActivityManager;
22 import android.app.ActivityManagerInternal;
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.database.ContentObserver;
27 import android.hardware.health.V1_0.HealthInfo;
28 import android.hardware.health.V2_0.IHealth;
29 import android.hardware.health.V2_0.IHealthInfoCallback;
30 import android.hardware.health.V2_0.Result;
31 import android.hidl.manager.V1_0.IServiceManager;
32 import android.hidl.manager.V1_0.IServiceNotification;
33 import android.metrics.LogMaker;
34 import android.os.BatteryManager;
35 import android.os.BatteryManagerInternal;
36 import android.os.BatteryProperty;
37 import android.os.BatteryStats;
38 import android.os.Binder;
39 import android.os.Bundle;
40 import android.os.DropBoxManager;
41 import android.os.FileUtils;
42 import android.os.Handler;
43 import android.os.HandlerThread;
44 import android.os.IBatteryPropertiesListener;
45 import android.os.IBatteryPropertiesRegistrar;
46 import android.os.IBinder;
47 import android.os.OsProtoEnums;
48 import android.os.PowerManager;
49 import android.os.RemoteException;
50 import android.os.ResultReceiver;
51 import android.os.ServiceManager;
52 import android.os.ShellCallback;
53 import android.os.ShellCommand;
54 import android.os.SystemClock;
55 import android.os.Trace;
56 import android.os.UEventObserver;
57 import android.os.UserHandle;
58 import android.provider.Settings;
59 import android.service.battery.BatteryServiceDumpProto;
60 import android.util.EventLog;
61 import android.util.MutableInt;
62 import android.util.Slog;
63 import android.util.proto.ProtoOutputStream;
64 
65 import com.android.internal.annotations.VisibleForTesting;
66 import com.android.internal.app.IBatteryStats;
67 import com.android.internal.logging.MetricsLogger;
68 import com.android.internal.util.DumpUtils;
69 import com.android.server.am.BatteryStatsService;
70 import com.android.server.lights.Light;
71 import com.android.server.lights.LightsManager;
72 
73 import java.io.File;
74 import java.io.FileDescriptor;
75 import java.io.FileOutputStream;
76 import java.io.IOException;
77 import java.io.PrintWriter;
78 import java.util.ArrayDeque;
79 import java.util.ArrayList;
80 import java.util.Arrays;
81 import java.util.List;
82 import java.util.NoSuchElementException;
83 import java.util.Objects;
84 import java.util.concurrent.atomic.AtomicReference;
85 
86 /**
87  * <p>BatteryService monitors the charging status, and charge level of the device
88  * battery.  When these values change this service broadcasts the new values
89  * to all {@link android.content.BroadcastReceiver IntentReceivers} that are
90  * watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED
91  * BATTERY_CHANGED} action.</p>
92  * <p>The new values are stored in the Intent data and can be retrieved by
93  * calling {@link android.content.Intent#getExtra Intent.getExtra} with the
94  * following keys:</p>
95  * <p>&quot;scale&quot; - int, the maximum value for the charge level</p>
96  * <p>&quot;level&quot; - int, charge level, from 0 through &quot;scale&quot; inclusive</p>
97  * <p>&quot;status&quot; - String, the current charging status.<br />
98  * <p>&quot;health&quot; - String, the current battery health.<br />
99  * <p>&quot;present&quot; - boolean, true if the battery is present<br />
100  * <p>&quot;icon-small&quot; - int, suggested small icon to use for this state</p>
101  * <p>&quot;plugged&quot; - int, 0 if the device is not plugged in; 1 if plugged
102  * into an AC power adapter; 2 if plugged in via USB.</p>
103  * <p>&quot;voltage&quot; - int, current battery voltage in millivolts</p>
104  * <p>&quot;temperature&quot; - int, current battery temperature in tenths of
105  * a degree Centigrade</p>
106  * <p>&quot;technology&quot; - String, the type of battery installed, e.g. "Li-ion"</p>
107  *
108  * <p>
109  * The battery service may be called by the power manager while holding its locks so
110  * we take care to post all outcalls into the activity manager to a handler.
111  *
112  * FIXME: Ideally the power manager would perform all of its calls into the battery
113  * service asynchronously itself.
114  * </p>
115  */
116 public final class BatteryService extends SystemService {
117     private static final String TAG = BatteryService.class.getSimpleName();
118 
119     private static final boolean DEBUG = false;
120 
121     private static final int BATTERY_SCALE = 100;    // battery capacity is a percentage
122 
123     private static final long HEALTH_HAL_WAIT_MS = 1000;
124     private static final long BATTERY_LEVEL_CHANGE_THROTTLE_MS = 60_000;
125     private static final int MAX_BATTERY_LEVELS_QUEUE_SIZE = 100;
126 
127     // Used locally for determining when to make a last ditch effort to log
128     // discharge stats before the device dies.
129     private int mCriticalBatteryLevel;
130 
131     private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" };
132 
133     private static final String DUMPSYS_DATA_PATH = "/data/system/";
134 
135     // This should probably be exposed in the API, though it's not critical
136     private static final int BATTERY_PLUGGED_NONE = OsProtoEnums.BATTERY_PLUGGED_NONE; // = 0
137 
138     private final Context mContext;
139     private final IBatteryStats mBatteryStats;
140     BinderService mBinderService;
141     private final Handler mHandler;
142 
143     private final Object mLock = new Object();
144 
145     private HealthInfo mHealthInfo;
146     private final HealthInfo mLastHealthInfo = new HealthInfo();
147     private boolean mBatteryLevelCritical;
148     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;
155     private int mLastMaxChargingCurrent;
156     private int mLastMaxChargingVoltage;
157     private int mLastChargeCounter;
158 
159     private int mSequence = 1;
160 
161     private int mInvalidCharger;
162     private int mLastInvalidCharger;
163 
164     private int mLowBatteryWarningLevel;
165     private int mLowBatteryCloseWarningLevel;
166     private int mShutdownBatteryTemperature;
167 
168     private int mPlugType;
169     private int mLastPlugType = -1; // Extra state so we can detect first run
170 
171     private boolean mBatteryLevelLow;
172 
173     private long mDischargeStartTime;
174     private int mDischargeStartLevel;
175 
176     private long mChargeStartTime;
177     private int mChargeStartLevel;
178 
179     private boolean mUpdatesStopped;
180 
181     private Led mLed;
182 
183     private boolean mSentLowBatteryBroadcast = false;
184 
185     private ActivityManagerInternal mActivityManagerInternal;
186 
187     private HealthServiceWrapper mHealthServiceWrapper;
188     private HealthHalCallback mHealthHalCallback;
189     private BatteryPropertiesRegistrar mBatteryPropertiesRegistrar;
190     private ArrayDeque<Bundle> mBatteryLevelsEventQueue;
191     private long mLastBatteryLevelChangedSentMs;
192 
193     private MetricsLogger mMetricsLogger;
194 
BatteryService(Context context)195     public BatteryService(Context context) {
196         super(context);
197 
198         mContext = context;
199         mHandler = new Handler(true /*async*/);
200         mLed = new Led(context, getLocalService(LightsManager.class));
201         mBatteryStats = BatteryStatsService.getService();
202         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
203 
204         mCriticalBatteryLevel = mContext.getResources().getInteger(
205                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
206         mLowBatteryWarningLevel = mContext.getResources().getInteger(
207                 com.android.internal.R.integer.config_lowBatteryWarningLevel);
208         mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
209                 com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
210         mShutdownBatteryTemperature = mContext.getResources().getInteger(
211                 com.android.internal.R.integer.config_shutdownBatteryTemperature);
212 
213         mBatteryLevelsEventQueue = new ArrayDeque<>();
214         mMetricsLogger = new MetricsLogger();
215 
216         // watch for invalid charger messages if the invalid_charger switch exists
217         if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
218             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(
230                     "DEVPATH=/devices/virtual/switch/invalid_charger");
231         }
232     }
233 
234     @Override
onStart()235     public void onStart() {
236         registerHealthCallback();
237 
238         mBinderService = new BinderService();
239         publishBinderService("battery", mBinderService);
240         mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
241         publishBinderService("batteryproperties", mBatteryPropertiesRegistrar);
242         publishLocalService(BatteryManagerInternal.class, new LocalService());
243     }
244 
245     @Override
onBootPhase(int phase)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                         }
256                     }
257                 };
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             }
264         }
265     }
266 
registerHealthCallback()267     private void registerHealthCallback() {
268         traceBegin("HealthInitWrapper");
269         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() {});
276         } 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;
282         } finally {
283             traceEnd();
284         }
285 
286         traceBegin("HealthInitWaitUpdate");
287         // 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.
290         long beforeWait = SystemClock.uptimeMillis();
291         synchronized (mLock) {
292             while (mHealthInfo == null) {
293                 Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait) +
294                         "ms for callbacks. Waiting another " + HEALTH_HAL_WAIT_MS + " ms...");
295                 try {
296                     mLock.wait(HEALTH_HAL_WAIT_MS);
297                 } catch (InterruptedException ex) {
298                     Slog.i(TAG, "health: InterruptedException when waiting for update. "
299                         + " Continuing...");
300                 }
301             }
302         }
303 
304         Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait)
305                 + "ms and received the update.");
306         traceEnd();
307     }
308 
updateBatteryWarningLevelLocked()309     private void updateBatteryWarningLevelLocked() {
310         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 
isPoweredLocked(int plugTypeSet)326     private boolean isPoweredLocked(int plugTypeSet) {
327         // assume we are powered if battery state is unknown so
328         // the "stay on while plugged in" option will work.
329         if (mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
330             return true;
331         }
332         if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mHealthInfo.chargerAcOnline) {
333             return true;
334         }
335         if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mHealthInfo.chargerUsbOnline) {
336             return true;
337         }
338         if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mHealthInfo.chargerWirelessOnline) {
339             return true;
340         }
341         return false;
342     }
343 
shouldSendBatteryLowLocked()344     private boolean shouldSendBatteryLowLocked() {
345         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
355                 && mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
356                 && mHealthInfo.batteryLevel <= mLowBatteryWarningLevel
357                 && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
358     }
359 
shutdownIfNoPowerLocked()360     private void shutdownIfNoPowerLocked() {
361         // shut down gracefully if our battery is critically low and we are not powered.
362         // wait until the system has booted before attempting to display the shutdown dialog.
363         if (mHealthInfo.batteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) {
364             mHandler.post(new Runnable() {
365                 @Override
366                 public void run() {
367                     if (mActivityManagerInternal.isSystemReady()) {
368                         Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
369                         intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
370                         intent.putExtra(Intent.EXTRA_REASON,
371                                 PowerManager.SHUTDOWN_LOW_BATTERY);
372                         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
373                         mContext.startActivityAsUser(intent, UserHandle.CURRENT);
374                     }
375                 }
376             });
377         }
378     }
379 
shutdownIfOverTempLocked()380     private void shutdownIfOverTempLocked() {
381         // shut down gracefully if temperature is too high (> 68.0C by default)
382         // wait until the system has booted before attempting to display the
383         // shutdown dialog.
384         if (mHealthInfo.batteryTemperature > mShutdownBatteryTemperature) {
385             mHandler.post(new Runnable() {
386                 @Override
387                 public void run() {
388                     if (mActivityManagerInternal.isSystemReady()) {
389                         Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
390                         intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
391                         intent.putExtra(Intent.EXTRA_REASON,
392                                 PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE);
393                         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
394                         mContext.startActivityAsUser(intent, UserHandle.CURRENT);
395                     }
396                 }
397             });
398         }
399     }
400 
update(android.hardware.health.V2_0.HealthInfo info)401     private void update(android.hardware.health.V2_0.HealthInfo info) {
402         traceBegin("HealthInfoUpdate");
403         synchronized (mLock) {
404             if (!mUpdatesStopped) {
405                 mHealthInfo = info.legacy;
406                 // Process the new values.
407                 processValuesLocked(false);
408                 mLock.notifyAll(); // for any waiters on new info
409             } else {
410                 copy(mLastHealthInfo, info.legacy);
411             }
412         }
413         traceEnd();
414     }
415 
copy(HealthInfo dst, HealthInfo src)416     private static void copy(HealthInfo dst, HealthInfo src) {
417         dst.chargerAcOnline = src.chargerAcOnline;
418         dst.chargerUsbOnline = src.chargerUsbOnline;
419         dst.chargerWirelessOnline = src.chargerWirelessOnline;
420         dst.maxChargingCurrent = src.maxChargingCurrent;
421         dst.maxChargingVoltage = src.maxChargingVoltage;
422         dst.batteryStatus = src.batteryStatus;
423         dst.batteryHealth = src.batteryHealth;
424         dst.batteryPresent = src.batteryPresent;
425         dst.batteryLevel = src.batteryLevel;
426         dst.batteryVoltage = src.batteryVoltage;
427         dst.batteryTemperature = src.batteryTemperature;
428         dst.batteryCurrent = src.batteryCurrent;
429         dst.batteryCycleCount = src.batteryCycleCount;
430         dst.batteryFullCharge = src.batteryFullCharge;
431         dst.batteryChargeCounter = src.batteryChargeCounter;
432         dst.batteryTechnology = src.batteryTechnology;
433     }
434 
processValuesLocked(boolean force)435     private void processValuesLocked(boolean force) {
436         boolean logOutlier = false;
437         long dischargeDuration = 0;
438 
439         mBatteryLevelCritical =
440             mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
441             && mHealthInfo.batteryLevel <= mCriticalBatteryLevel;
442         if (mHealthInfo.chargerAcOnline) {
443             mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
444         } else if (mHealthInfo.chargerUsbOnline) {
445             mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
446         } else if (mHealthInfo.chargerWirelessOnline) {
447             mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
448         } else {
449             mPlugType = BATTERY_PLUGGED_NONE;
450         }
451 
452         if (DEBUG) {
453             Slog.d(TAG, "Processing new values: "
454                     + "info=" + mHealthInfo
455                     + ", mBatteryLevelCritical=" + mBatteryLevelCritical
456                     + ", mPlugType=" + mPlugType);
457         }
458 
459         // Let the battery stats keep track of the current level.
460         try {
461             mBatteryStats.setBatteryState(mHealthInfo.batteryStatus, mHealthInfo.batteryHealth,
462                     mPlugType, mHealthInfo.batteryLevel, mHealthInfo.batteryTemperature,
463                     mHealthInfo.batteryVoltage, mHealthInfo.batteryChargeCounter,
464                     mHealthInfo.batteryFullCharge);
465         } catch (RemoteException e) {
466             // Should never happen.
467         }
468 
469         shutdownIfNoPowerLocked();
470         shutdownIfOverTempLocked();
471 
472         if (force || (mHealthInfo.batteryStatus != mLastBatteryStatus ||
473                 mHealthInfo.batteryHealth != mLastBatteryHealth ||
474                 mHealthInfo.batteryPresent != mLastBatteryPresent ||
475                 mHealthInfo.batteryLevel != mLastBatteryLevel ||
476                 mPlugType != mLastPlugType ||
477                 mHealthInfo.batteryVoltage != mLastBatteryVoltage ||
478                 mHealthInfo.batteryTemperature != mLastBatteryTemperature ||
479                 mHealthInfo.maxChargingCurrent != mLastMaxChargingCurrent ||
480                 mHealthInfo.maxChargingVoltage != mLastMaxChargingVoltage ||
481                 mHealthInfo.batteryChargeCounter != mLastChargeCounter ||
482                 mInvalidCharger != mLastInvalidCharger)) {
483 
484             if (mPlugType != mLastPlugType) {
485                 if (mLastPlugType == BATTERY_PLUGGED_NONE) {
486                     // discharging -> charging
487                     mChargeStartLevel = mHealthInfo.batteryLevel;
488                     mChargeStartTime = SystemClock.elapsedRealtime();
489 
490                     final LogMaker builder = new LogMaker(MetricsEvent.ACTION_CHARGE);
491                     builder.setType(MetricsEvent.TYPE_ACTION);
492                     builder.addTaggedData(MetricsEvent.FIELD_PLUG_TYPE, mPlugType);
493                     builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_START,
494                             mHealthInfo.batteryLevel);
495                     mMetricsLogger.write(builder);
496 
497                     // There's no value in this data unless we've discharged at least once and the
498                     // battery level has changed; so don't log until it does.
499                     if (mDischargeStartTime != 0 && mDischargeStartLevel != mHealthInfo.batteryLevel) {
500                         dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
501                         logOutlier = true;
502                         EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
503                                 mDischargeStartLevel, mHealthInfo.batteryLevel);
504                         // make sure we see a discharge event before logging again
505                         mDischargeStartTime = 0;
506                     }
507                 } else if (mPlugType == BATTERY_PLUGGED_NONE) {
508                     // charging -> discharging or we just powered up
509                     mDischargeStartTime = SystemClock.elapsedRealtime();
510                     mDischargeStartLevel = mHealthInfo.batteryLevel;
511 
512                     long chargeDuration = SystemClock.elapsedRealtime() - mChargeStartTime;
513                     if (mChargeStartTime != 0 && chargeDuration != 0) {
514                         final LogMaker builder = new LogMaker(MetricsEvent.ACTION_CHARGE);
515                         builder.setType(MetricsEvent.TYPE_DISMISS);
516                         builder.addTaggedData(MetricsEvent.FIELD_PLUG_TYPE, mLastPlugType);
517                         builder.addTaggedData(MetricsEvent.FIELD_CHARGING_DURATION_MILLIS,
518                                 chargeDuration);
519                         builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_START,
520                                 mChargeStartLevel);
521                         builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_END,
522                                 mHealthInfo.batteryLevel);
523                         mMetricsLogger.write(builder);
524                     }
525                     mChargeStartTime = 0;
526                 }
527             }
528             if (mHealthInfo.batteryStatus != mLastBatteryStatus ||
529                     mHealthInfo.batteryHealth != mLastBatteryHealth ||
530                     mHealthInfo.batteryPresent != mLastBatteryPresent ||
531                     mPlugType != mLastPlugType) {
532                 EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
533                         mHealthInfo.batteryStatus, mHealthInfo.batteryHealth, mHealthInfo.batteryPresent ? 1 : 0,
534                         mPlugType, mHealthInfo.batteryTechnology);
535             }
536             if (mHealthInfo.batteryLevel != mLastBatteryLevel) {
537                 // Don't do this just from voltage or temperature changes, that is
538                 // too noisy.
539                 EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
540                         mHealthInfo.batteryLevel, mHealthInfo.batteryVoltage, mHealthInfo.batteryTemperature);
541             }
542             if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
543                     mPlugType == BATTERY_PLUGGED_NONE) {
544                 // We want to make sure we log discharge cycle outliers
545                 // if the battery is about to die.
546                 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
547                 logOutlier = true;
548             }
549 
550             if (!mBatteryLevelLow) {
551                 // Should we now switch in to low battery mode?
552                 if (mPlugType == BATTERY_PLUGGED_NONE
553                         && mHealthInfo.batteryStatus !=
554                            BatteryManager.BATTERY_STATUS_UNKNOWN
555                         && mHealthInfo.batteryLevel <= mLowBatteryWarningLevel) {
556                     mBatteryLevelLow = true;
557                 }
558             } else {
559                 // Should we now switch out of low battery mode?
560                 if (mPlugType != BATTERY_PLUGGED_NONE) {
561                     mBatteryLevelLow = false;
562                 } else if (mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel)  {
563                     mBatteryLevelLow = false;
564                 } else if (force && mHealthInfo.batteryLevel >= mLowBatteryWarningLevel) {
565                     // If being forced, the previous state doesn't matter, we will just
566                     // absolutely check to see if we are now above the warning level.
567                     mBatteryLevelLow = false;
568                 }
569             }
570 
571             mSequence++;
572 
573             // Separate broadcast is sent for power connected / not connected
574             // since the standard intent will not wake any applications and some
575             // applications may want to have smart behavior based on this.
576             if (mPlugType != 0 && mLastPlugType == 0) {
577                 final Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);
578                 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
579                 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
580                 mHandler.post(new Runnable() {
581                     @Override
582                     public void run() {
583                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
584                     }
585                 });
586             }
587             else if (mPlugType == 0 && mLastPlugType != 0) {
588                 final Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
589                 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
590                 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
591                 mHandler.post(new Runnable() {
592                     @Override
593                     public void run() {
594                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
595                     }
596                 });
597             }
598 
599             if (shouldSendBatteryLowLocked()) {
600                 mSentLowBatteryBroadcast = true;
601                 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);
602                 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
603                 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
604                 mHandler.post(new Runnable() {
605                     @Override
606                     public void run() {
607                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
608                     }
609                 });
610             } else if (mSentLowBatteryBroadcast &&
611                     mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) {
612                 mSentLowBatteryBroadcast = false;
613                 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
614                 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
615                 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
616                 mHandler.post(new Runnable() {
617                     @Override
618                     public void run() {
619                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
620                     }
621                 });
622             }
623 
624             // We are doing this after sending the above broadcasts, so anything processing
625             // them will get the new sequence number at that point.  (See for example how testing
626             // of JobScheduler's BatteryController works.)
627             sendBatteryChangedIntentLocked();
628             if (mLastBatteryLevel != mHealthInfo.batteryLevel) {
629                 sendBatteryLevelChangedIntentLocked();
630             }
631 
632 
633             // Update the battery LED
634             mLed.updateLightsLocked();
635 
636             // This needs to be done after sendIntent() so that we get the lastest battery stats.
637             if (logOutlier && dischargeDuration != 0) {
638                 logOutlierLocked(dischargeDuration);
639             }
640 
641             mLastBatteryStatus = mHealthInfo.batteryStatus;
642             mLastBatteryHealth = mHealthInfo.batteryHealth;
643             mLastBatteryPresent = mHealthInfo.batteryPresent;
644             mLastBatteryLevel = mHealthInfo.batteryLevel;
645             mLastPlugType = mPlugType;
646             mLastBatteryVoltage = mHealthInfo.batteryVoltage;
647             mLastBatteryTemperature = mHealthInfo.batteryTemperature;
648             mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrent;
649             mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltage;
650             mLastChargeCounter = mHealthInfo.batteryChargeCounter;
651             mLastBatteryLevelCritical = mBatteryLevelCritical;
652             mLastInvalidCharger = mInvalidCharger;
653         }
654     }
655 
sendBatteryChangedIntentLocked()656     private void sendBatteryChangedIntentLocked() {
657         //  Pack up the values and broadcast them to everyone
658         final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
659         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
660                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
661 
662         int icon = getIconLocked(mHealthInfo.batteryLevel);
663 
664         intent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
665         intent.putExtra(BatteryManager.EXTRA_STATUS, mHealthInfo.batteryStatus);
666         intent.putExtra(BatteryManager.EXTRA_HEALTH, mHealthInfo.batteryHealth);
667         intent.putExtra(BatteryManager.EXTRA_PRESENT, mHealthInfo.batteryPresent);
668         intent.putExtra(BatteryManager.EXTRA_LEVEL, mHealthInfo.batteryLevel);
669         intent.putExtra(BatteryManager.EXTRA_BATTERY_LOW, mSentLowBatteryBroadcast);
670         intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
671         intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
672         intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
673         intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage);
674         intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperature);
675         intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mHealthInfo.batteryTechnology);
676         intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
677         intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent);
678         intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltage);
679         intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
680         if (DEBUG) {
681             Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. scale:" + BATTERY_SCALE
682                     + ", info:" + mHealthInfo.toString());
683         }
684 
685         mHandler.post(() -> ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL));
686     }
687 
sendBatteryLevelChangedIntentLocked()688     private void sendBatteryLevelChangedIntentLocked() {
689         Bundle event = new Bundle();
690         long now = SystemClock.elapsedRealtime();
691         event.putInt(BatteryManager.EXTRA_SEQUENCE, mSequence);
692         event.putInt(BatteryManager.EXTRA_STATUS, mHealthInfo.batteryStatus);
693         event.putInt(BatteryManager.EXTRA_HEALTH, mHealthInfo.batteryHealth);
694         event.putBoolean(BatteryManager.EXTRA_PRESENT, mHealthInfo.batteryPresent);
695         event.putInt(BatteryManager.EXTRA_LEVEL, mHealthInfo.batteryLevel);
696         event.putBoolean(BatteryManager.EXTRA_BATTERY_LOW, mSentLowBatteryBroadcast);
697         event.putInt(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
698         event.putInt(BatteryManager.EXTRA_PLUGGED, mPlugType);
699         event.putInt(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage);
700         event.putLong(BatteryManager.EXTRA_EVENT_TIMESTAMP, now);
701 
702         boolean queueWasEmpty = mBatteryLevelsEventQueue.isEmpty();
703         mBatteryLevelsEventQueue.add(event);
704         // Make sure queue is bounded and doesn't exceed intent payload limits
705         if (mBatteryLevelsEventQueue.size() > MAX_BATTERY_LEVELS_QUEUE_SIZE) {
706             mBatteryLevelsEventQueue.removeFirst();
707         }
708 
709         if (queueWasEmpty) {
710             // send now if last event was before throttle interval, otherwise delay
711             long delay = now - mLastBatteryLevelChangedSentMs > BATTERY_LEVEL_CHANGE_THROTTLE_MS
712                     ? 0 : mLastBatteryLevelChangedSentMs + BATTERY_LEVEL_CHANGE_THROTTLE_MS - now;
713             mHandler.postDelayed(this::sendEnqueuedBatteryLevelChangedEvents, delay);
714         }
715     }
716 
sendEnqueuedBatteryLevelChangedEvents()717     private void sendEnqueuedBatteryLevelChangedEvents() {
718         ArrayList<Bundle> events;
719         synchronized (mLock) {
720             events = new ArrayList<>(mBatteryLevelsEventQueue);
721             mBatteryLevelsEventQueue.clear();
722         }
723         final Intent intent = new Intent(Intent.ACTION_BATTERY_LEVEL_CHANGED);
724         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
725         intent.putParcelableArrayListExtra(BatteryManager.EXTRA_EVENTS, events);
726 
727         mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
728                 android.Manifest.permission.BATTERY_STATS);
729         mLastBatteryLevelChangedSentMs = SystemClock.elapsedRealtime();
730     }
731 
logBatteryStatsLocked()732     private void logBatteryStatsLocked() {
733         IBinder batteryInfoService = ServiceManager.getService(BatteryStats.SERVICE_NAME);
734         if (batteryInfoService == null) return;
735 
736         DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
737         if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return;
738 
739         File dumpFile = null;
740         FileOutputStream dumpStream = null;
741         try {
742             // dump the service to a file
743             dumpFile = new File(DUMPSYS_DATA_PATH + BatteryStats.SERVICE_NAME + ".dump");
744             dumpStream = new FileOutputStream(dumpFile);
745             batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);
746             FileUtils.sync(dumpStream);
747 
748             // add dump file to drop box
749             db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT);
750         } catch (RemoteException e) {
751             Slog.e(TAG, "failed to dump battery service", e);
752         } catch (IOException e) {
753             Slog.e(TAG, "failed to write dumpsys file", e);
754         } finally {
755             // make sure we clean up
756             if (dumpStream != null) {
757                 try {
758                     dumpStream.close();
759                 } catch (IOException e) {
760                     Slog.e(TAG, "failed to close dumpsys output stream");
761                 }
762             }
763             if (dumpFile != null && !dumpFile.delete()) {
764                 Slog.e(TAG, "failed to delete temporary dumpsys file: "
765                         + dumpFile.getAbsolutePath());
766             }
767         }
768     }
769 
logOutlierLocked(long duration)770     private void logOutlierLocked(long duration) {
771         ContentResolver cr = mContext.getContentResolver();
772         String dischargeThresholdString = Settings.Global.getString(cr,
773                 Settings.Global.BATTERY_DISCHARGE_THRESHOLD);
774         String durationThresholdString = Settings.Global.getString(cr,
775                 Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD);
776 
777         if (dischargeThresholdString != null && durationThresholdString != null) {
778             try {
779                 long durationThreshold = Long.parseLong(durationThresholdString);
780                 int dischargeThreshold = Integer.parseInt(dischargeThresholdString);
781                 if (duration <= durationThreshold &&
782                         mDischargeStartLevel - mHealthInfo.batteryLevel >= dischargeThreshold) {
783                     // If the discharge cycle is bad enough we want to know about it.
784                     logBatteryStatsLocked();
785                 }
786                 if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +
787                         " discharge threshold: " + dischargeThreshold);
788                 if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +
789                         (mDischargeStartLevel - mHealthInfo.batteryLevel));
790             } catch (NumberFormatException e) {
791                 Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
792                         durationThresholdString + " or " + dischargeThresholdString);
793             }
794         }
795     }
796 
getIconLocked(int level)797     private int getIconLocked(int level) {
798         if (mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
799             return com.android.internal.R.drawable.stat_sys_battery_charge;
800         } else if (mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
801             return com.android.internal.R.drawable.stat_sys_battery;
802         } else if (mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
803                 || mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
804             if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)
805                     && mHealthInfo.batteryLevel >= 100) {
806                 return com.android.internal.R.drawable.stat_sys_battery_charge;
807             } else {
808                 return com.android.internal.R.drawable.stat_sys_battery;
809             }
810         } else {
811             return com.android.internal.R.drawable.stat_sys_battery_unknown;
812         }
813     }
814 
815     class Shell extends ShellCommand {
816         @Override
onCommand(String cmd)817         public int onCommand(String cmd) {
818             return onShellCommand(this, cmd);
819         }
820 
821         @Override
onHelp()822         public void onHelp() {
823             PrintWriter pw = getOutPrintWriter();
824             dumpHelp(pw);
825         }
826     }
827 
dumpHelp(PrintWriter pw)828     static void dumpHelp(PrintWriter pw) {
829         pw.println("Battery service (battery) commands:");
830         pw.println("  help");
831         pw.println("    Print this help text.");
832         pw.println("  set [-f] [ac|usb|wireless|status|level|temp|present|invalid] <value>");
833         pw.println("    Force a battery property value, freezing battery state.");
834         pw.println("    -f: force a battery change broadcast be sent, prints new sequence.");
835         pw.println("  unplug [-f]");
836         pw.println("    Force battery unplugged, freezing battery state.");
837         pw.println("    -f: force a battery change broadcast be sent, prints new sequence.");
838         pw.println("  reset [-f]");
839         pw.println("    Unfreeze battery state, returning to current hardware values.");
840         pw.println("    -f: force a battery change broadcast be sent, prints new sequence.");
841     }
842 
843     static final int OPTION_FORCE_UPDATE = 1<<0;
844 
parseOptions(Shell shell)845     int parseOptions(Shell shell) {
846         String opt;
847         int opts = 0;
848         while ((opt = shell.getNextOption()) != null) {
849             if ("-f".equals(opt)) {
850                 opts |= OPTION_FORCE_UPDATE;
851             }
852         }
853         return opts;
854     }
855 
onShellCommand(Shell shell, String cmd)856     int onShellCommand(Shell shell, String cmd) {
857         if (cmd == null) {
858             return shell.handleDefaultCommands(cmd);
859         }
860         PrintWriter pw = shell.getOutPrintWriter();
861         switch (cmd) {
862             case "unplug": {
863                 int opts = parseOptions(shell);
864                 getContext().enforceCallingOrSelfPermission(
865                         android.Manifest.permission.DEVICE_POWER, null);
866                 if (!mUpdatesStopped) {
867                     copy(mLastHealthInfo, mHealthInfo);
868                 }
869                 mHealthInfo.chargerAcOnline = false;
870                 mHealthInfo.chargerUsbOnline = false;
871                 mHealthInfo.chargerWirelessOnline = false;
872                 long ident = Binder.clearCallingIdentity();
873                 try {
874                     mUpdatesStopped = true;
875                     processValuesFromShellLocked(pw, opts);
876                 } finally {
877                     Binder.restoreCallingIdentity(ident);
878                 }
879             } break;
880             case "set": {
881                 int opts = parseOptions(shell);
882                 getContext().enforceCallingOrSelfPermission(
883                         android.Manifest.permission.DEVICE_POWER, null);
884                 final String key = shell.getNextArg();
885                 if (key == null) {
886                     pw.println("No property specified");
887                     return -1;
888 
889                 }
890                 final String value = shell.getNextArg();
891                 if (value == null) {
892                     pw.println("No value specified");
893                     return -1;
894 
895                 }
896                 try {
897                     if (!mUpdatesStopped) {
898                         copy(mLastHealthInfo, mHealthInfo);
899                     }
900                     boolean update = true;
901                     switch (key) {
902                         case "present":
903                             mHealthInfo.batteryPresent = Integer.parseInt(value) != 0;
904                             break;
905                         case "ac":
906                             mHealthInfo.chargerAcOnline = Integer.parseInt(value) != 0;
907                             break;
908                         case "usb":
909                             mHealthInfo.chargerUsbOnline = Integer.parseInt(value) != 0;
910                             break;
911                         case "wireless":
912                             mHealthInfo.chargerWirelessOnline = Integer.parseInt(value) != 0;
913                             break;
914                         case "status":
915                             mHealthInfo.batteryStatus = Integer.parseInt(value);
916                             break;
917                         case "level":
918                             mHealthInfo.batteryLevel = Integer.parseInt(value);
919                             break;
920                         case "counter":
921                             mHealthInfo.batteryChargeCounter = Integer.parseInt(value);
922                             break;
923                         case "temp":
924                             mHealthInfo.batteryTemperature = Integer.parseInt(value);
925                             break;
926                         case "invalid":
927                             mInvalidCharger = Integer.parseInt(value);
928                             break;
929                         default:
930                             pw.println("Unknown set option: " + key);
931                             update = false;
932                             break;
933                     }
934                     if (update) {
935                         long ident = Binder.clearCallingIdentity();
936                         try {
937                             mUpdatesStopped = true;
938                             processValuesFromShellLocked(pw, opts);
939                         } finally {
940                             Binder.restoreCallingIdentity(ident);
941                         }
942                     }
943                 } catch (NumberFormatException ex) {
944                     pw.println("Bad value: " + value);
945                     return -1;
946                 }
947             } break;
948             case "reset": {
949                 int opts = parseOptions(shell);
950                 getContext().enforceCallingOrSelfPermission(
951                         android.Manifest.permission.DEVICE_POWER, null);
952                 long ident = Binder.clearCallingIdentity();
953                 try {
954                     if (mUpdatesStopped) {
955                         mUpdatesStopped = false;
956                         copy(mHealthInfo, mLastHealthInfo);
957                         processValuesFromShellLocked(pw, opts);
958                     }
959                 } finally {
960                     Binder.restoreCallingIdentity(ident);
961                 }
962             } break;
963             default:
964                 return shell.handleDefaultCommands(cmd);
965         }
966         return 0;
967     }
968 
processValuesFromShellLocked(PrintWriter pw, int opts)969     private void processValuesFromShellLocked(PrintWriter pw, int opts) {
970         processValuesLocked((opts & OPTION_FORCE_UPDATE) != 0);
971         if ((opts & OPTION_FORCE_UPDATE) != 0) {
972             pw.println(mSequence);
973         }
974     }
975 
dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args)976     private void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
977         synchronized (mLock) {
978             if (args == null || args.length == 0 || "-a".equals(args[0])) {
979                 pw.println("Current Battery Service state:");
980                 if (mUpdatesStopped) {
981                     pw.println("  (UPDATES STOPPED -- use 'reset' to restart)");
982                 }
983                 pw.println("  AC powered: " + mHealthInfo.chargerAcOnline);
984                 pw.println("  USB powered: " + mHealthInfo.chargerUsbOnline);
985                 pw.println("  Wireless powered: " + mHealthInfo.chargerWirelessOnline);
986                 pw.println("  Max charging current: " + mHealthInfo.maxChargingCurrent);
987                 pw.println("  Max charging voltage: " + mHealthInfo.maxChargingVoltage);
988                 pw.println("  Charge counter: " + mHealthInfo.batteryChargeCounter);
989                 pw.println("  status: " + mHealthInfo.batteryStatus);
990                 pw.println("  health: " + mHealthInfo.batteryHealth);
991                 pw.println("  present: " + mHealthInfo.batteryPresent);
992                 pw.println("  level: " + mHealthInfo.batteryLevel);
993                 pw.println("  scale: " + BATTERY_SCALE);
994                 pw.println("  voltage: " + mHealthInfo.batteryVoltage);
995                 pw.println("  temperature: " + mHealthInfo.batteryTemperature);
996                 pw.println("  technology: " + mHealthInfo.batteryTechnology);
997             } else {
998                 Shell shell = new Shell();
999                 shell.exec(mBinderService, null, fd, null, args, null, new ResultReceiver(null));
1000             }
1001         }
1002     }
1003 
dumpProto(FileDescriptor fd)1004     private void dumpProto(FileDescriptor fd) {
1005         final ProtoOutputStream proto = new ProtoOutputStream(fd);
1006 
1007         synchronized (mLock) {
1008             proto.write(BatteryServiceDumpProto.ARE_UPDATES_STOPPED, mUpdatesStopped);
1009             int batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_NONE;
1010             if (mHealthInfo.chargerAcOnline) {
1011                 batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_AC;
1012             } else if (mHealthInfo.chargerUsbOnline) {
1013                 batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_USB;
1014             } else if (mHealthInfo.chargerWirelessOnline) {
1015                 batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_WIRELESS;
1016             }
1017             proto.write(BatteryServiceDumpProto.PLUGGED, batteryPluggedValue);
1018             proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent);
1019             proto.write(BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltage);
1020             proto.write(BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
1021             proto.write(BatteryServiceDumpProto.STATUS, mHealthInfo.batteryStatus);
1022             proto.write(BatteryServiceDumpProto.HEALTH, mHealthInfo.batteryHealth);
1023             proto.write(BatteryServiceDumpProto.IS_PRESENT, mHealthInfo.batteryPresent);
1024             proto.write(BatteryServiceDumpProto.LEVEL, mHealthInfo.batteryLevel);
1025             proto.write(BatteryServiceDumpProto.SCALE, BATTERY_SCALE);
1026             proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.batteryVoltage);
1027             proto.write(BatteryServiceDumpProto.TEMPERATURE, mHealthInfo.batteryTemperature);
1028             proto.write(BatteryServiceDumpProto.TECHNOLOGY, mHealthInfo.batteryTechnology);
1029         }
1030         proto.flush();
1031     }
1032 
traceBegin(String name)1033     private static void traceBegin(String name) {
1034         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
1035     }
1036 
traceEnd()1037     private static void traceEnd() {
1038         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
1039     }
1040 
1041     private final class Led {
1042         private final Light mBatteryLight;
1043 
1044         private final int mBatteryLowARGB;
1045         private final int mBatteryMediumARGB;
1046         private final int mBatteryFullARGB;
1047         private final int mBatteryLedOn;
1048         private final int mBatteryLedOff;
1049 
Led(Context context, LightsManager lights)1050         public Led(Context context, LightsManager lights) {
1051             mBatteryLight = lights.getLight(LightsManager.LIGHT_ID_BATTERY);
1052 
1053             mBatteryLowARGB = context.getResources().getInteger(
1054                     com.android.internal.R.integer.config_notificationsBatteryLowARGB);
1055             mBatteryMediumARGB = context.getResources().getInteger(
1056                     com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
1057             mBatteryFullARGB = context.getResources().getInteger(
1058                     com.android.internal.R.integer.config_notificationsBatteryFullARGB);
1059             mBatteryLedOn = context.getResources().getInteger(
1060                     com.android.internal.R.integer.config_notificationsBatteryLedOn);
1061             mBatteryLedOff = context.getResources().getInteger(
1062                     com.android.internal.R.integer.config_notificationsBatteryLedOff);
1063         }
1064 
1065         /**
1066          * Synchronize on BatteryService.
1067          */
updateLightsLocked()1068         public void updateLightsLocked() {
1069             final int level = mHealthInfo.batteryLevel;
1070             final int status = mHealthInfo.batteryStatus;
1071             if (level < mLowBatteryWarningLevel) {
1072                 if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
1073                     // Solid red when battery is charging
1074                     mBatteryLight.setColor(mBatteryLowARGB);
1075                 } else {
1076                     // Flash red when battery is low and not charging
1077                     mBatteryLight.setFlashing(mBatteryLowARGB, Light.LIGHT_FLASH_TIMED,
1078                             mBatteryLedOn, mBatteryLedOff);
1079                 }
1080             } else if (status == BatteryManager.BATTERY_STATUS_CHARGING
1081                     || status == BatteryManager.BATTERY_STATUS_FULL) {
1082                 if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
1083                     // Solid green when full or charging and nearly full
1084                     mBatteryLight.setColor(mBatteryFullARGB);
1085                 } else {
1086                     // Solid orange when charging and halfway full
1087                     mBatteryLight.setColor(mBatteryMediumARGB);
1088                 }
1089             } else {
1090                 // No lights if not charging and not low
1091                 mBatteryLight.turnOff();
1092             }
1093         }
1094     }
1095 
1096     private final class HealthHalCallback extends IHealthInfoCallback.Stub
1097             implements HealthServiceWrapper.Callback {
healthInfoChanged(android.hardware.health.V2_0.HealthInfo props)1098         @Override public void healthInfoChanged(android.hardware.health.V2_0.HealthInfo props) {
1099             BatteryService.this.update(props);
1100         }
1101         // on new service registered
onRegistration(IHealth oldService, IHealth newService, String instance)1102         @Override public void onRegistration(IHealth oldService, IHealth newService,
1103                 String instance) {
1104             if (newService == null) return;
1105 
1106             traceBegin("HealthUnregisterCallback");
1107             try {
1108                 if (oldService != null) {
1109                     int r = oldService.unregisterCallback(this);
1110                     if (r != Result.SUCCESS) {
1111                         Slog.w(TAG, "health: cannot unregister previous callback: " +
1112                                 Result.toString(r));
1113                     }
1114                 }
1115             } catch (RemoteException ex) {
1116                 Slog.w(TAG, "health: cannot unregister previous callback (transaction error): "
1117                             + ex.getMessage());
1118             } finally {
1119                 traceEnd();
1120             }
1121 
1122             traceBegin("HealthRegisterCallback");
1123             try {
1124                 int r = newService.registerCallback(this);
1125                 if (r != Result.SUCCESS) {
1126                     Slog.w(TAG, "health: cannot register callback: " + Result.toString(r));
1127                     return;
1128                 }
1129                 // registerCallback does NOT guarantee that update is called
1130                 // immediately, so request a manual update here.
1131                 newService.update();
1132             } catch (RemoteException ex) {
1133                 Slog.e(TAG, "health: cannot register callback (transaction error): "
1134                         + ex.getMessage());
1135             } finally {
1136                 traceEnd();
1137             }
1138         }
1139     }
1140 
1141     private final class BinderService extends Binder {
dump(FileDescriptor fd, PrintWriter pw, String[] args)1142         @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1143             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
1144 
1145             if (args.length > 0 && "--proto".equals(args[0])) {
1146                 dumpProto(fd);
1147             } else {
1148                 dumpInternal(fd, pw, args);
1149             }
1150         }
1151 
onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)1152         @Override public void onShellCommand(FileDescriptor in, FileDescriptor out,
1153                 FileDescriptor err, String[] args, ShellCallback callback,
1154                 ResultReceiver resultReceiver) {
1155             (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
1156         }
1157     }
1158 
1159     // Reduced IBatteryPropertiesRegistrar that only implements getProperty for usage
1160     // in BatteryManager.
1161     private final class BatteryPropertiesRegistrar extends IBatteryPropertiesRegistrar.Stub {
registerListener(IBatteryPropertiesListener listener)1162         public void registerListener(IBatteryPropertiesListener listener) {
1163             Slog.e(TAG, "health: must not call registerListener on battery properties");
1164         }
unregisterListener(IBatteryPropertiesListener listener)1165         public void unregisterListener(IBatteryPropertiesListener listener) {
1166             Slog.e(TAG, "health: must not call unregisterListener on battery properties");
1167         }
getProperty(int id, final BatteryProperty prop)1168         public int getProperty(int id, final BatteryProperty prop) throws RemoteException {
1169             traceBegin("HealthGetProperty");
1170             try {
1171                 IHealth service = mHealthServiceWrapper.getLastService();
1172                 if (service == null) throw new RemoteException("no health service");
1173                 final MutableInt outResult = new MutableInt(Result.NOT_SUPPORTED);
1174                 switch(id) {
1175                     case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER:
1176                         service.getChargeCounter((int result, int value) -> {
1177                             outResult.value = result;
1178                             if (result == Result.SUCCESS) prop.setLong(value);
1179                         });
1180                         break;
1181                     case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW:
1182                         service.getCurrentNow((int result, int value) -> {
1183                             outResult.value = result;
1184                             if (result == Result.SUCCESS) prop.setLong(value);
1185                         });
1186                         break;
1187                     case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE:
1188                         service.getCurrentAverage((int result, int value) -> {
1189                             outResult.value = result;
1190                             if (result == Result.SUCCESS) prop.setLong(value);
1191                         });
1192                         break;
1193                     case BatteryManager.BATTERY_PROPERTY_CAPACITY:
1194                         service.getCapacity((int result, int value) -> {
1195                             outResult.value = result;
1196                             if (result == Result.SUCCESS) prop.setLong(value);
1197                         });
1198                         break;
1199                     case BatteryManager.BATTERY_PROPERTY_STATUS:
1200                         service.getChargeStatus((int result, int value) -> {
1201                             outResult.value = result;
1202                             if (result == Result.SUCCESS) prop.setLong(value);
1203                         });
1204                         break;
1205                     case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER:
1206                         service.getEnergyCounter((int result, long value) -> {
1207                             outResult.value = result;
1208                             if (result == Result.SUCCESS) prop.setLong(value);
1209                         });
1210                         break;
1211                 }
1212                 return outResult.value;
1213             } finally {
1214                 traceEnd();
1215             }
1216         }
scheduleUpdate()1217         public void scheduleUpdate() throws RemoteException {
1218             traceBegin("HealthScheduleUpdate");
1219             try {
1220                 IHealth service = mHealthServiceWrapper.getLastService();
1221                 if (service == null) throw new RemoteException("no health service");
1222                 service.update();
1223             } finally {
1224                 traceEnd();
1225             }
1226         }
1227     }
1228 
1229     private final class LocalService extends BatteryManagerInternal {
1230         @Override
isPowered(int plugTypeSet)1231         public boolean isPowered(int plugTypeSet) {
1232             synchronized (mLock) {
1233                 return isPoweredLocked(plugTypeSet);
1234             }
1235         }
1236 
1237         @Override
getPlugType()1238         public int getPlugType() {
1239             synchronized (mLock) {
1240                 return mPlugType;
1241             }
1242         }
1243 
1244         @Override
getBatteryLevel()1245         public int getBatteryLevel() {
1246             synchronized (mLock) {
1247                 return mHealthInfo.batteryLevel;
1248             }
1249         }
1250 
1251         @Override
getBatteryChargeCounter()1252         public int getBatteryChargeCounter() {
1253             synchronized (mLock) {
1254                 return mHealthInfo.batteryChargeCounter;
1255             }
1256         }
1257 
1258         @Override
getBatteryFullCharge()1259         public int getBatteryFullCharge() {
1260             synchronized (mLock) {
1261                 return mHealthInfo.batteryFullCharge;
1262             }
1263         }
1264 
1265         @Override
getBatteryLevelLow()1266         public boolean getBatteryLevelLow() {
1267             synchronized (mLock) {
1268                 return mBatteryLevelLow;
1269             }
1270         }
1271 
1272         @Override
getInvalidCharger()1273         public int getInvalidCharger() {
1274             synchronized (mLock) {
1275                 return mInvalidCharger;
1276             }
1277         }
1278     }
1279 
1280     /**
1281      * HealthServiceWrapper wraps the internal IHealth service and refreshes the service when
1282      * necessary.
1283      *
1284      * On new registration of IHealth service, {@link #onRegistration onRegistration} is called and
1285      * the internal service is refreshed.
1286      * On death of an existing IHealth service, the internal service is NOT cleared to avoid
1287      * race condition between death notification and new service notification. Hence,
1288      * a caller must check for transaction errors when calling into the service.
1289      *
1290      * @hide Should only be used internally.
1291      */
1292     @VisibleForTesting
1293     static final class HealthServiceWrapper {
1294         private static final String TAG = "HealthServiceWrapper";
1295         public static final String INSTANCE_HEALTHD = "backup";
1296         public static final String INSTANCE_VENDOR = "default";
1297         // All interesting instances, sorted by priority high -> low.
1298         private static final List<String> sAllInstances =
1299                 Arrays.asList(INSTANCE_VENDOR, INSTANCE_HEALTHD);
1300 
1301         private final IServiceNotification mNotification = new Notification();
1302         private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceRefresh");
1303         // These variables are fixed after init.
1304         private Callback mCallback;
1305         private IHealthSupplier mHealthSupplier;
1306         private String mInstanceName;
1307 
1308         // Last IHealth service received.
1309         private final AtomicReference<IHealth> mLastService = new AtomicReference<>();
1310 
1311         /**
1312          * init should be called after constructor. For testing purposes, init is not called by
1313          * constructor.
1314          */
HealthServiceWrapper()1315         HealthServiceWrapper() {
1316         }
1317 
getLastService()1318         IHealth getLastService() {
1319             return mLastService.get();
1320         }
1321 
1322         /**
1323          * Start monitoring registration of new IHealth services. Only instances that are in
1324          * {@code sAllInstances} and in device / framework manifest are used. This function should
1325          * only be called once.
1326          *
1327          * mCallback.onRegistration() is called synchronously (aka in init thread) before
1328          * this method returns.
1329          *
1330          * @throws RemoteException transaction error when talking to IServiceManager
1331          * @throws NoSuchElementException if one of the following cases:
1332          *         - No service manager;
1333          *         - none of {@code sAllInstances} are in manifests (i.e. not
1334          *           available on this device), or none of these instances are available to current
1335          *           process.
1336          * @throws NullPointerException when callback is null or supplier is null
1337          */
init(Callback callback, IServiceManagerSupplier managerSupplier, IHealthSupplier healthSupplier)1338         void init(Callback callback,
1339                   IServiceManagerSupplier managerSupplier,
1340                   IHealthSupplier healthSupplier)
1341                 throws RemoteException, NoSuchElementException, NullPointerException {
1342             if (callback == null || managerSupplier == null || healthSupplier == null)
1343                 throw new NullPointerException();
1344 
1345             IServiceManager manager;
1346 
1347             mCallback = callback;
1348             mHealthSupplier = healthSupplier;
1349 
1350             // Initialize mLastService and call callback for the first time (in init thread)
1351             IHealth newService = null;
1352             for (String name : sAllInstances) {
1353                 traceBegin("HealthInitGetService_" + name);
1354                 try {
1355                     newService = healthSupplier.get(name);
1356                 } catch (NoSuchElementException ex) {
1357                     /* ignored, handled below */
1358                 } finally {
1359                     traceEnd();
1360                 }
1361                 if (newService != null) {
1362                     mInstanceName = name;
1363                     mLastService.set(newService);
1364                     break;
1365                 }
1366             }
1367 
1368             if (mInstanceName == null || newService == null) {
1369                 throw new NoSuchElementException(String.format(
1370                         "No IHealth service instance among %s is available. Perhaps no permission?",
1371                         sAllInstances.toString()));
1372             }
1373             mCallback.onRegistration(null, newService, mInstanceName);
1374 
1375             // Register for future service registrations
1376             traceBegin("HealthInitRegisterNotification");
1377             mHandlerThread.start();
1378             try {
1379                 managerSupplier.get().registerForNotifications(
1380                         IHealth.kInterfaceName, mInstanceName, mNotification);
1381             } finally {
1382                 traceEnd();
1383             }
1384             Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + mInstanceName);
1385         }
1386 
1387         @VisibleForTesting
getHandlerThread()1388         HandlerThread getHandlerThread() {
1389             return mHandlerThread;
1390         }
1391 
1392         interface Callback {
1393             /**
1394              * This function is invoked asynchronously when a new and related IServiceNotification
1395              * is received.
1396              * @param service the recently retrieved service from IServiceManager.
1397              * Can be a dead service before service notification of a new service is delivered.
1398              * Implementation must handle cases for {@link RemoteException}s when calling
1399              * into service.
1400              * @param instance instance name.
1401              */
onRegistration(IHealth oldService, IHealth newService, String instance)1402             void onRegistration(IHealth oldService, IHealth newService, String instance);
1403         }
1404 
1405         /**
1406          * Supplier of services.
1407          * Must not return null; throw {@link NoSuchElementException} if a service is not available.
1408          */
1409         interface IServiceManagerSupplier {
get()1410             default IServiceManager get() throws NoSuchElementException, RemoteException {
1411                 return IServiceManager.getService();
1412             }
1413         }
1414         /**
1415          * Supplier of services.
1416          * Must not return null; throw {@link NoSuchElementException} if a service is not available.
1417          */
1418         interface IHealthSupplier {
get(String name)1419             default IHealth get(String name) throws NoSuchElementException, RemoteException {
1420                 return IHealth.getService(name, true /* retry */);
1421             }
1422         }
1423 
1424         private class Notification extends IServiceNotification.Stub {
1425             @Override
onRegistration(String interfaceName, String instanceName, boolean preexisting)1426             public final void onRegistration(String interfaceName, String instanceName,
1427                     boolean preexisting) {
1428                 if (!IHealth.kInterfaceName.equals(interfaceName)) return;
1429                 if (!mInstanceName.equals(instanceName)) return;
1430 
1431                 // This runnable only runs on mHandlerThread and ordering is ensured, hence
1432                 // no locking is needed inside the runnable.
1433                 mHandlerThread.getThreadHandler().post(new Runnable() {
1434                     @Override
1435                     public void run() {
1436                         try {
1437                             IHealth newService = mHealthSupplier.get(mInstanceName);
1438                             IHealth oldService = mLastService.getAndSet(newService);
1439 
1440                             // preexisting may be inaccurate (race). Check for equality here.
1441                             if (Objects.equals(newService, oldService)) return;
1442 
1443                             Slog.i(TAG, "health: new instance registered " + mInstanceName);
1444                             mCallback.onRegistration(oldService, newService, mInstanceName);
1445                         } catch (NoSuchElementException | RemoteException ex) {
1446                             Slog.e(TAG, "health: Cannot get instance '" + mInstanceName
1447                                     + "': " + ex.getMessage() + ". Perhaps no permission?");
1448                         }
1449                     }
1450                 });
1451             }
1452         }
1453     }
1454 }
1455