• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import static com.android.server.health.Utils.copyV1Battery;
21 
22 import android.annotation.Nullable;
23 import android.app.ActivityManager;
24 import android.app.ActivityManagerInternal;
25 import android.content.ContentResolver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.database.ContentObserver;
29 import android.hardware.health.HealthInfo;
30 import android.hardware.health.V2_1.BatteryCapacityLevel;
31 import android.metrics.LogMaker;
32 import android.os.BatteryManager;
33 import android.os.BatteryManagerInternal;
34 import android.os.BatteryProperty;
35 import android.os.BatteryStats;
36 import android.os.Binder;
37 import android.os.Build;
38 import android.os.Bundle;
39 import android.os.DropBoxManager;
40 import android.os.FileUtils;
41 import android.os.Handler;
42 import android.os.IBatteryPropertiesRegistrar;
43 import android.os.IBinder;
44 import android.os.OsProtoEnums;
45 import android.os.PowerManager;
46 import android.os.RemoteException;
47 import android.os.ResultReceiver;
48 import android.os.ServiceManager;
49 import android.os.ShellCallback;
50 import android.os.ShellCommand;
51 import android.os.SystemClock;
52 import android.os.Trace;
53 import android.os.UEventObserver;
54 import android.os.UserHandle;
55 import android.provider.Settings;
56 import android.service.battery.BatteryServiceDumpProto;
57 import android.sysprop.PowerProperties;
58 import android.util.EventLog;
59 import android.util.Slog;
60 import android.util.proto.ProtoOutputStream;
61 
62 import com.android.internal.app.IBatteryStats;
63 import com.android.internal.logging.MetricsLogger;
64 import com.android.internal.util.DumpUtils;
65 import com.android.server.am.BatteryStatsService;
66 import com.android.server.health.HealthServiceWrapper;
67 import com.android.server.lights.LightsManager;
68 import com.android.server.lights.LogicalLight;
69 
70 import java.io.File;
71 import java.io.FileDescriptor;
72 import java.io.FileOutputStream;
73 import java.io.IOException;
74 import java.io.PrintWriter;
75 import java.util.ArrayDeque;
76 import java.util.ArrayList;
77 import java.util.NoSuchElementException;
78 
79 /**
80  * <p>BatteryService monitors the charging status, and charge level of the device
81  * battery.  When these values change this service broadcasts the new values
82  * to all {@link android.content.BroadcastReceiver IntentReceivers} that are
83  * watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED
84  * BATTERY_CHANGED} action.</p>
85  * <p>The new values are stored in the Intent data and can be retrieved by
86  * calling {@link android.content.Intent#getExtra Intent.getExtra} with the
87  * following keys:</p>
88  * <p>&quot;scale&quot; - int, the maximum value for the charge level</p>
89  * <p>&quot;level&quot; - int, charge level, from 0 through &quot;scale&quot; inclusive</p>
90  * <p>&quot;status&quot; - String, the current charging status.<br />
91  * <p>&quot;health&quot; - String, the current battery health.<br />
92  * <p>&quot;present&quot; - boolean, true if the battery is present<br />
93  * <p>&quot;icon-small&quot; - int, suggested small icon to use for this state</p>
94  * <p>&quot;plugged&quot; - int, 0 if the device is not plugged in; 1 if plugged
95  * into an AC power adapter; 2 if plugged in via USB.</p>
96  * <p>&quot;voltage&quot; - int, current battery voltage in millivolts</p>
97  * <p>&quot;temperature&quot; - int, current battery temperature in tenths of
98  * a degree Centigrade</p>
99  * <p>&quot;technology&quot; - String, the type of battery installed, e.g. "Li-ion"</p>
100  *
101  * <p>
102  * The battery service may be called by the power manager while holding its locks so
103  * we take care to post all outcalls into the activity manager to a handler.
104  *
105  * FIXME: Ideally the power manager would perform all of its calls into the battery
106  * service asynchronously itself.
107  * </p>
108  */
109 public final class BatteryService extends SystemService {
110     private static final String TAG = BatteryService.class.getSimpleName();
111 
112     private static final boolean DEBUG = false;
113 
114     private static final int BATTERY_SCALE = 100;    // battery capacity is a percentage
115 
116     private static final long HEALTH_HAL_WAIT_MS = 1000;
117     private static final long BATTERY_LEVEL_CHANGE_THROTTLE_MS = 60_000;
118     private static final int MAX_BATTERY_LEVELS_QUEUE_SIZE = 100;
119 
120     // Used locally for determining when to make a last ditch effort to log
121     // discharge stats before the device dies.
122     private int mCriticalBatteryLevel;
123 
124     // TODO: Current args don't work since "--unplugged" flag was purposefully removed.
125     private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" };
126 
127     private static final String DUMPSYS_DATA_PATH = "/data/system/";
128 
129     // This should probably be exposed in the API, though it's not critical
130     private static final int BATTERY_PLUGGED_NONE = OsProtoEnums.BATTERY_PLUGGED_NONE; // = 0
131 
132     private final Context mContext;
133     private final IBatteryStats mBatteryStats;
134     BinderService mBinderService;
135     private final Handler mHandler;
136 
137     private final Object mLock = new Object();
138 
139     private HealthInfo mHealthInfo;
140     private final HealthInfo mLastHealthInfo = new HealthInfo();
141     private boolean mBatteryLevelCritical;
142     private int mLastBatteryStatus;
143     private int mLastBatteryHealth;
144     private boolean mLastBatteryPresent;
145     private int mLastBatteryLevel;
146     private int mLastBatteryVoltage;
147     private int mLastBatteryTemperature;
148     private boolean mLastBatteryLevelCritical;
149     private int mLastMaxChargingCurrent;
150     private int mLastMaxChargingVoltage;
151     private int mLastChargeCounter;
152 
153     private int mSequence = 1;
154 
155     private int mInvalidCharger;
156     private int mLastInvalidCharger;
157 
158     private int mLowBatteryWarningLevel;
159     private int mLastLowBatteryWarningLevel;
160     private int mLowBatteryCloseWarningLevel;
161     private int mShutdownBatteryTemperature;
162 
163     private int mPlugType;
164     private int mLastPlugType = -1; // Extra state so we can detect first run
165 
166     private boolean mBatteryLevelLow;
167 
168     private long mDischargeStartTime;
169     private int mDischargeStartLevel;
170 
171     private long mChargeStartTime;
172     private int mChargeStartLevel;
173 
174     private boolean mUpdatesStopped;
175     private boolean mBatteryInputSuspended;
176 
177     private Led mLed;
178 
179     private boolean mSentLowBatteryBroadcast = false;
180 
181     private ActivityManagerInternal mActivityManagerInternal;
182 
183     private HealthServiceWrapper mHealthServiceWrapper;
184     private BatteryPropertiesRegistrar mBatteryPropertiesRegistrar;
185     private ArrayDeque<Bundle> mBatteryLevelsEventQueue;
186     private long mLastBatteryLevelChangedSentMs;
187 
188     private MetricsLogger mMetricsLogger;
189 
BatteryService(Context context)190     public BatteryService(Context context) {
191         super(context);
192 
193         mContext = context;
194         mHandler = new Handler(true /*async*/);
195         mLed = new Led(context, getLocalService(LightsManager.class));
196         mBatteryStats = BatteryStatsService.getService();
197         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
198 
199         mCriticalBatteryLevel = mContext.getResources().getInteger(
200                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
201         mLowBatteryWarningLevel = mContext.getResources().getInteger(
202                 com.android.internal.R.integer.config_lowBatteryWarningLevel);
203         mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
204                 com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
205         mShutdownBatteryTemperature = mContext.getResources().getInteger(
206                 com.android.internal.R.integer.config_shutdownBatteryTemperature);
207 
208         mBatteryLevelsEventQueue = new ArrayDeque<>();
209         mMetricsLogger = new MetricsLogger();
210 
211         // watch for invalid charger messages if the invalid_charger switch exists
212         if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
213             UEventObserver invalidChargerObserver = new UEventObserver() {
214                 @Override
215                 public void onUEvent(UEvent event) {
216                     final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
217                     synchronized (mLock) {
218                         if (mInvalidCharger != invalidCharger) {
219                             mInvalidCharger = invalidCharger;
220                         }
221                     }
222                 }
223             };
224             invalidChargerObserver.startObserving(
225                     "DEVPATH=/devices/virtual/switch/invalid_charger");
226         }
227 
228         mBatteryInputSuspended = PowerProperties.battery_input_suspended().orElse(false);
229     }
230 
231     @Override
onStart()232     public void onStart() {
233         registerHealthCallback();
234 
235         mBinderService = new BinderService();
236         publishBinderService("battery", mBinderService);
237         mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
238         publishBinderService("batteryproperties", mBatteryPropertiesRegistrar);
239         publishLocalService(BatteryManagerInternal.class, new LocalService());
240     }
241 
242     @Override
onBootPhase(int phase)243     public void onBootPhase(int phase) {
244         if (phase == PHASE_ACTIVITY_MANAGER_READY) {
245             // check our power situation now that it is safe to display the shutdown dialog.
246             synchronized (mLock) {
247                 ContentObserver obs = new ContentObserver(mHandler) {
248                     @Override
249                     public void onChange(boolean selfChange) {
250                         synchronized (mLock) {
251                             updateBatteryWarningLevelLocked();
252                         }
253                     }
254                 };
255                 final ContentResolver resolver = mContext.getContentResolver();
256                 resolver.registerContentObserver(Settings.Global.getUriFor(
257                         Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
258                         false, obs, UserHandle.USER_ALL);
259                 updateBatteryWarningLevelLocked();
260             }
261         }
262     }
263 
registerHealthCallback()264     private void registerHealthCallback() {
265         traceBegin("HealthInitWrapper");
266         // IHealth is lazily retrieved.
267         try {
268             mHealthServiceWrapper = HealthServiceWrapper.create(this::update);
269         } catch (RemoteException ex) {
270             Slog.e(TAG, "health: cannot register callback. (RemoteException)");
271             throw ex.rethrowFromSystemServer();
272         } catch (NoSuchElementException ex) {
273             Slog.e(TAG, "health: cannot register callback. (no supported health HAL service)");
274             throw ex;
275         } finally {
276             traceEnd();
277         }
278 
279         traceBegin("HealthInitWaitUpdate");
280         // init register for new service notifications, and IServiceManager should return the
281         // existing service in a near future. Wait for this.update() to instantiate
282         // the initial mHealthInfo.
283         long beforeWait = SystemClock.uptimeMillis();
284         synchronized (mLock) {
285             while (mHealthInfo == null) {
286                 Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait) +
287                         "ms for callbacks. Waiting another " + HEALTH_HAL_WAIT_MS + " ms...");
288                 try {
289                     mLock.wait(HEALTH_HAL_WAIT_MS);
290                 } catch (InterruptedException ex) {
291                     Slog.i(TAG, "health: InterruptedException when waiting for update. "
292                         + " Continuing...");
293                 }
294             }
295         }
296 
297         Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait)
298                 + "ms and received the update.");
299         traceEnd();
300     }
301 
updateBatteryWarningLevelLocked()302     private void updateBatteryWarningLevelLocked() {
303         final ContentResolver resolver = mContext.getContentResolver();
304         int defWarnLevel = mContext.getResources().getInteger(
305                 com.android.internal.R.integer.config_lowBatteryWarningLevel);
306         mLastLowBatteryWarningLevel = mLowBatteryWarningLevel;
307         mLowBatteryWarningLevel = Settings.Global.getInt(resolver,
308                 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
309         if (mLowBatteryWarningLevel == 0) {
310             mLowBatteryWarningLevel = defWarnLevel;
311         }
312         if (mLowBatteryWarningLevel < mCriticalBatteryLevel) {
313             mLowBatteryWarningLevel = mCriticalBatteryLevel;
314         }
315         mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
316                 com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
317         processValuesLocked(true);
318     }
319 
isPoweredLocked(int plugTypeSet)320     private boolean isPoweredLocked(int plugTypeSet) {
321         // assume we are powered if battery state is unknown so
322         // the "stay on while plugged in" option will work.
323         if (mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
324             return true;
325         }
326         if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0
327                 && mHealthInfo.chargerAcOnline) {
328             return true;
329         }
330         if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0
331                 && mHealthInfo.chargerUsbOnline) {
332             return true;
333         }
334         if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0
335                 && mHealthInfo.chargerWirelessOnline) {
336             return true;
337         }
338         if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_DOCK) != 0
339                 && mHealthInfo.chargerDockOnline) {
340             return true;
341         }
342         return false;
343     }
344 
shouldSendBatteryLowLocked()345     private boolean shouldSendBatteryLowLocked() {
346         final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
347         final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
348 
349         /* The ACTION_BATTERY_LOW broadcast is sent in these situations:
350          * - is just un-plugged (previously was plugged) and battery level is
351          *   less than or equal to WARNING, or
352          * - is not plugged and battery level falls to WARNING boundary
353          *   (becomes <= mLowBatteryWarningLevel).
354          */
355         return !plugged
356                 && mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
357                 && mHealthInfo.batteryLevel <= mLowBatteryWarningLevel
358                 && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel
359                     || mHealthInfo.batteryLevel > mLastLowBatteryWarningLevel);
360     }
361 
shouldShutdownLocked()362     private boolean shouldShutdownLocked() {
363         if (mHealthInfo.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) {
364             return (mHealthInfo.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL);
365         }
366         if (mHealthInfo.batteryLevel > 0) {
367             return false;
368         }
369 
370         // Battery-less devices should not shutdown.
371         if (!mHealthInfo.batteryPresent) {
372             return false;
373         }
374 
375         // If battery state is not CHARGING, shutdown.
376         // - If battery present and state == unknown, this is an unexpected error state.
377         // - If level <= 0 and state == full, this is also an unexpected state
378         // - All other states (NOT_CHARGING, DISCHARGING) means it is not charging.
379         return mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_CHARGING;
380     }
381 
shutdownIfNoPowerLocked()382     private void shutdownIfNoPowerLocked() {
383         // shut down gracefully if our battery is critically low and we are not powered.
384         // wait until the system has booted before attempting to display the shutdown dialog.
385         if (shouldShutdownLocked()) {
386             mHandler.post(new Runnable() {
387                 @Override
388                 public void run() {
389                     if (mActivityManagerInternal.isSystemReady()) {
390                         Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
391                         intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
392                         intent.putExtra(Intent.EXTRA_REASON,
393                                 PowerManager.SHUTDOWN_LOW_BATTERY);
394                         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
395                         mContext.startActivityAsUser(intent, UserHandle.CURRENT);
396                     }
397                 }
398             });
399         }
400     }
401 
shutdownIfOverTempLocked()402     private void shutdownIfOverTempLocked() {
403         // shut down gracefully if temperature is too high (> 68.0C by default)
404         // wait until the system has booted before attempting to display the
405         // shutdown dialog.
406         if (mHealthInfo.batteryTemperatureTenthsCelsius > mShutdownBatteryTemperature) {
407             mHandler.post(new Runnable() {
408                 @Override
409                 public void run() {
410                     if (mActivityManagerInternal.isSystemReady()) {
411                         Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
412                         intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
413                         intent.putExtra(Intent.EXTRA_REASON,
414                                 PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE);
415                         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
416                         mContext.startActivityAsUser(intent, UserHandle.CURRENT);
417                     }
418                 }
419             });
420         }
421     }
422 
update(android.hardware.health.HealthInfo info)423     private void update(android.hardware.health.HealthInfo info) {
424         traceBegin("HealthInfoUpdate");
425 
426         Trace.traceCounter(
427                 Trace.TRACE_TAG_POWER, "BatteryChargeCounter", info.batteryChargeCounterUah);
428         Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent", info.batteryCurrentMicroamps);
429         Trace.traceCounter(Trace.TRACE_TAG_POWER, "PlugType", plugType(info));
430         Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryStatus", info.batteryStatus);
431 
432         synchronized (mLock) {
433             if (!mUpdatesStopped) {
434                 mHealthInfo = info;
435                 // Process the new values.
436                 processValuesLocked(false);
437                 mLock.notifyAll(); // for any waiters on new info
438             } else {
439                 copyV1Battery(mLastHealthInfo, info);
440             }
441         }
442         traceEnd();
443     }
444 
plugType(HealthInfo healthInfo)445     private static int plugType(HealthInfo healthInfo) {
446         if (healthInfo.chargerAcOnline) {
447             return BatteryManager.BATTERY_PLUGGED_AC;
448         } else if (healthInfo.chargerUsbOnline) {
449             return BatteryManager.BATTERY_PLUGGED_USB;
450         } else if (healthInfo.chargerWirelessOnline) {
451             return BatteryManager.BATTERY_PLUGGED_WIRELESS;
452         } else if (healthInfo.chargerDockOnline) {
453             return BatteryManager.BATTERY_PLUGGED_DOCK;
454         } else {
455             return BATTERY_PLUGGED_NONE;
456         }
457     }
458 
processValuesLocked(boolean force)459     private void processValuesLocked(boolean force) {
460         boolean logOutlier = false;
461         long dischargeDuration = 0;
462 
463         mBatteryLevelCritical =
464             mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
465             && mHealthInfo.batteryLevel <= mCriticalBatteryLevel;
466         mPlugType = plugType(mHealthInfo);
467 
468         if (DEBUG) {
469             Slog.d(TAG, "Processing new values: "
470                     + "info=" + mHealthInfo
471                     + ", mBatteryLevelCritical=" + mBatteryLevelCritical
472                     + ", mPlugType=" + mPlugType);
473         }
474 
475         // Let the battery stats keep track of the current level.
476         try {
477             mBatteryStats.setBatteryState(
478                     mHealthInfo.batteryStatus,
479                     mHealthInfo.batteryHealth,
480                     mPlugType,
481                     mHealthInfo.batteryLevel,
482                     mHealthInfo.batteryTemperatureTenthsCelsius,
483                     mHealthInfo.batteryVoltageMillivolts,
484                     mHealthInfo.batteryChargeCounterUah,
485                     mHealthInfo.batteryFullChargeUah,
486                     mHealthInfo.batteryChargeTimeToFullNowSeconds);
487         } catch (RemoteException e) {
488             // Should never happen.
489         }
490 
491         shutdownIfNoPowerLocked();
492         shutdownIfOverTempLocked();
493 
494         if (force
495                 || (mHealthInfo.batteryStatus != mLastBatteryStatus
496                         || mHealthInfo.batteryHealth != mLastBatteryHealth
497                         || mHealthInfo.batteryPresent != mLastBatteryPresent
498                         || mHealthInfo.batteryLevel != mLastBatteryLevel
499                         || mPlugType != mLastPlugType
500                         || mHealthInfo.batteryVoltageMillivolts != mLastBatteryVoltage
501                         || mHealthInfo.batteryTemperatureTenthsCelsius != mLastBatteryTemperature
502                         || mHealthInfo.maxChargingCurrentMicroamps != mLastMaxChargingCurrent
503                         || mHealthInfo.maxChargingVoltageMicrovolts != mLastMaxChargingVoltage
504                         || mHealthInfo.batteryChargeCounterUah != mLastChargeCounter
505                         || mInvalidCharger != mLastInvalidCharger)) {
506 
507             if (mPlugType != mLastPlugType) {
508                 if (mLastPlugType == BATTERY_PLUGGED_NONE) {
509                     // discharging -> charging
510                     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);
519 
520                     // 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.
522                     if (mDischargeStartTime != 0 && mDischargeStartLevel != mHealthInfo.batteryLevel) {
523                         dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
524                         logOutlier = true;
525                         EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
526                                 mDischargeStartLevel, mHealthInfo.batteryLevel);
527                         // make sure we see a discharge event before logging again
528                         mDischargeStartTime = 0;
529                     }
530                 } else if (mPlugType == BATTERY_PLUGGED_NONE) {
531                     // charging -> discharging or we just powered up
532                     mDischargeStartTime = SystemClock.elapsedRealtime();
533                     mDischargeStartLevel = mHealthInfo.batteryLevel;
534 
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;
549                 }
550             }
551             if (mHealthInfo.batteryStatus != mLastBatteryStatus ||
552                     mHealthInfo.batteryHealth != mLastBatteryHealth ||
553                     mHealthInfo.batteryPresent != mLastBatteryPresent ||
554                     mPlugType != mLastPlugType) {
555                 EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
556                         mHealthInfo.batteryStatus, mHealthInfo.batteryHealth, mHealthInfo.batteryPresent ? 1 : 0,
557                         mPlugType, mHealthInfo.batteryTechnology);
558             }
559             if (mHealthInfo.batteryLevel != mLastBatteryLevel) {
560                 // Don't do this just from voltage or temperature changes, that is
561                 // too noisy.
562                 EventLog.writeEvent(
563                         EventLogTags.BATTERY_LEVEL,
564                         mHealthInfo.batteryLevel,
565                         mHealthInfo.batteryVoltageMillivolts,
566                         mHealthInfo.batteryTemperatureTenthsCelsius);
567             }
568             if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
569                     mPlugType == BATTERY_PLUGGED_NONE) {
570                 // We want to make sure we log discharge cycle outliers
571                 // if the battery is about to die.
572                 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
573                 logOutlier = true;
574             }
575 
576             if (!mBatteryLevelLow) {
577                 // Should we now switch in to low battery mode?
578                 if (mPlugType == BATTERY_PLUGGED_NONE
579                         && mHealthInfo.batteryStatus !=
580                            BatteryManager.BATTERY_STATUS_UNKNOWN
581                         && mHealthInfo.batteryLevel <= mLowBatteryWarningLevel) {
582                     mBatteryLevelLow = true;
583                 }
584             } else {
585                 // Should we now switch out of low battery mode?
586                 if (mPlugType != BATTERY_PLUGGED_NONE) {
587                     mBatteryLevelLow = false;
588                 } else if (mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel)  {
589                     mBatteryLevelLow = false;
590                 } else if (force && mHealthInfo.batteryLevel >= mLowBatteryWarningLevel) {
591                     // If being forced, the previous state doesn't matter, we will just
592                     // absolutely check to see if we are now above the warning level.
593                     mBatteryLevelLow = false;
594                 }
595             }
596 
597             mSequence++;
598 
599             // Separate broadcast is sent for power connected / not connected
600             // since the standard intent will not wake any applications and some
601             // applications may want to have smart behavior based on this.
602             if (mPlugType != 0 && mLastPlugType == 0) {
603                 final Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);
604                 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
605                 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
606                 mHandler.post(new Runnable() {
607                     @Override
608                     public void run() {
609                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
610                     }
611                 });
612             }
613             else if (mPlugType == 0 && mLastPlugType != 0) {
614                 final Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
615                 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
616                 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
617                 mHandler.post(new Runnable() {
618                     @Override
619                     public void run() {
620                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
621                     }
622                 });
623             }
624 
625             if (shouldSendBatteryLowLocked()) {
626                 mSentLowBatteryBroadcast = true;
627                 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);
628                 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
629                 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
630                 mHandler.post(new Runnable() {
631                     @Override
632                     public void run() {
633                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
634                     }
635                 });
636             } else if (mSentLowBatteryBroadcast &&
637                     mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) {
638                 mSentLowBatteryBroadcast = false;
639                 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
640                 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
641                 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
642                 mHandler.post(new Runnable() {
643                     @Override
644                     public void run() {
645                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
646                     }
647                 });
648             }
649 
650             // We are doing this after sending the above broadcasts, so anything processing
651             // them will get the new sequence number at that point.  (See for example how testing
652             // of JobScheduler's BatteryController works.)
653             sendBatteryChangedIntentLocked();
654             if (mLastBatteryLevel != mHealthInfo.batteryLevel || mLastPlugType != mPlugType) {
655                 sendBatteryLevelChangedIntentLocked();
656             }
657 
658 
659             // Update the battery LED
660             mLed.updateLightsLocked();
661 
662             // This needs to be done after sendIntent() so that we get the lastest battery stats.
663             if (logOutlier && dischargeDuration != 0) {
664                 logOutlierLocked(dischargeDuration);
665             }
666 
667             mLastBatteryStatus = mHealthInfo.batteryStatus;
668             mLastBatteryHealth = mHealthInfo.batteryHealth;
669             mLastBatteryPresent = mHealthInfo.batteryPresent;
670             mLastBatteryLevel = mHealthInfo.batteryLevel;
671             mLastPlugType = mPlugType;
672             mLastBatteryVoltage = mHealthInfo.batteryVoltageMillivolts;
673             mLastBatteryTemperature = mHealthInfo.batteryTemperatureTenthsCelsius;
674             mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrentMicroamps;
675             mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltageMicrovolts;
676             mLastChargeCounter = mHealthInfo.batteryChargeCounterUah;
677             mLastBatteryLevelCritical = mBatteryLevelCritical;
678             mLastInvalidCharger = mInvalidCharger;
679         }
680     }
681 
sendBatteryChangedIntentLocked()682     private void sendBatteryChangedIntentLocked() {
683         //  Pack up the values and broadcast them to everyone
684         final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
685         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
686                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
687 
688         int icon = getIconLocked(mHealthInfo.batteryLevel);
689 
690         intent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
691         intent.putExtra(BatteryManager.EXTRA_STATUS, mHealthInfo.batteryStatus);
692         intent.putExtra(BatteryManager.EXTRA_HEALTH, mHealthInfo.batteryHealth);
693         intent.putExtra(BatteryManager.EXTRA_PRESENT, mHealthInfo.batteryPresent);
694         intent.putExtra(BatteryManager.EXTRA_LEVEL, mHealthInfo.batteryLevel);
695         intent.putExtra(BatteryManager.EXTRA_BATTERY_LOW, mSentLowBatteryBroadcast);
696         intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
697         intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
698         intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
699         intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltageMillivolts);
700         intent.putExtra(
701                 BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperatureTenthsCelsius);
702         intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mHealthInfo.batteryTechnology);
703         intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
704         intent.putExtra(
705                 BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrentMicroamps);
706         intent.putExtra(
707                 BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE,
708                 mHealthInfo.maxChargingVoltageMicrovolts);
709         intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah);
710         if (DEBUG) {
711             Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. scale:" + BATTERY_SCALE
712                     + ", info:" + mHealthInfo.toString());
713         }
714 
715         mHandler.post(() -> ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL));
716     }
717 
sendBatteryLevelChangedIntentLocked()718     private void sendBatteryLevelChangedIntentLocked() {
719         Bundle event = new Bundle();
720         long now = SystemClock.elapsedRealtime();
721         event.putInt(BatteryManager.EXTRA_SEQUENCE, mSequence);
722         event.putInt(BatteryManager.EXTRA_STATUS, mHealthInfo.batteryStatus);
723         event.putInt(BatteryManager.EXTRA_HEALTH, mHealthInfo.batteryHealth);
724         event.putBoolean(BatteryManager.EXTRA_PRESENT, mHealthInfo.batteryPresent);
725         event.putInt(BatteryManager.EXTRA_LEVEL, mHealthInfo.batteryLevel);
726         event.putBoolean(BatteryManager.EXTRA_BATTERY_LOW, mSentLowBatteryBroadcast);
727         event.putInt(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
728         event.putInt(BatteryManager.EXTRA_PLUGGED, mPlugType);
729         event.putInt(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltageMillivolts);
730         event.putInt(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperatureTenthsCelsius);
731         event.putInt(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah);
732         event.putLong(BatteryManager.EXTRA_EVENT_TIMESTAMP, now);
733 
734         boolean queueWasEmpty = mBatteryLevelsEventQueue.isEmpty();
735         mBatteryLevelsEventQueue.add(event);
736         // Make sure queue is bounded and doesn't exceed intent payload limits
737         if (mBatteryLevelsEventQueue.size() > MAX_BATTERY_LEVELS_QUEUE_SIZE) {
738             mBatteryLevelsEventQueue.removeFirst();
739         }
740 
741         if (queueWasEmpty) {
742             // send now if last event was before throttle interval, otherwise delay
743             long delay = now - mLastBatteryLevelChangedSentMs > BATTERY_LEVEL_CHANGE_THROTTLE_MS
744                     ? 0 : mLastBatteryLevelChangedSentMs + BATTERY_LEVEL_CHANGE_THROTTLE_MS - now;
745             mHandler.postDelayed(this::sendEnqueuedBatteryLevelChangedEvents, delay);
746         }
747     }
748 
sendEnqueuedBatteryLevelChangedEvents()749     private void sendEnqueuedBatteryLevelChangedEvents() {
750         ArrayList<Bundle> events;
751         synchronized (mLock) {
752             events = new ArrayList<>(mBatteryLevelsEventQueue);
753             mBatteryLevelsEventQueue.clear();
754         }
755         final Intent intent = new Intent(Intent.ACTION_BATTERY_LEVEL_CHANGED);
756         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
757         intent.putParcelableArrayListExtra(BatteryManager.EXTRA_EVENTS, events);
758 
759         mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
760                 android.Manifest.permission.BATTERY_STATS);
761         mLastBatteryLevelChangedSentMs = SystemClock.elapsedRealtime();
762     }
763 
764     // TODO: Current code doesn't work since "--unplugged" flag in BSS was purposefully removed.
logBatteryStatsLocked()765     private void logBatteryStatsLocked() {
766         IBinder batteryInfoService = ServiceManager.getService(BatteryStats.SERVICE_NAME);
767         if (batteryInfoService == null) return;
768 
769         DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
770         if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return;
771 
772         File dumpFile = null;
773         FileOutputStream dumpStream = null;
774         try {
775             // dump the service to a file
776             dumpFile = new File(DUMPSYS_DATA_PATH + BatteryStats.SERVICE_NAME + ".dump");
777             dumpStream = new FileOutputStream(dumpFile);
778             batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);
779             FileUtils.sync(dumpStream);
780 
781             // add dump file to drop box
782             db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT);
783         } catch (RemoteException e) {
784             Slog.e(TAG, "failed to dump battery service", e);
785         } catch (IOException e) {
786             Slog.e(TAG, "failed to write dumpsys file", e);
787         } finally {
788             // make sure we clean up
789             if (dumpStream != null) {
790                 try {
791                     dumpStream.close();
792                 } catch (IOException e) {
793                     Slog.e(TAG, "failed to close dumpsys output stream");
794                 }
795             }
796             if (dumpFile != null && !dumpFile.delete()) {
797                 Slog.e(TAG, "failed to delete temporary dumpsys file: "
798                         + dumpFile.getAbsolutePath());
799             }
800         }
801     }
802 
logOutlierLocked(long duration)803     private void logOutlierLocked(long duration) {
804         ContentResolver cr = mContext.getContentResolver();
805         String dischargeThresholdString = Settings.Global.getString(cr,
806                 Settings.Global.BATTERY_DISCHARGE_THRESHOLD);
807         String durationThresholdString = Settings.Global.getString(cr,
808                 Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD);
809 
810         if (dischargeThresholdString != null && durationThresholdString != null) {
811             try {
812                 long durationThreshold = Long.parseLong(durationThresholdString);
813                 int dischargeThreshold = Integer.parseInt(dischargeThresholdString);
814                 if (duration <= durationThreshold &&
815                         mDischargeStartLevel - mHealthInfo.batteryLevel >= dischargeThreshold) {
816                     // If the discharge cycle is bad enough we want to know about it.
817                     logBatteryStatsLocked();
818                 }
819                 if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +
820                         " discharge threshold: " + dischargeThreshold);
821                 if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +
822                         (mDischargeStartLevel - mHealthInfo.batteryLevel));
823             } catch (NumberFormatException e) {
824                 Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
825                         durationThresholdString + " or " + dischargeThresholdString);
826             }
827         }
828     }
829 
getIconLocked(int level)830     private int getIconLocked(int level) {
831         if (mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
832             return com.android.internal.R.drawable.stat_sys_battery_charge;
833         } else if (mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
834             return com.android.internal.R.drawable.stat_sys_battery;
835         } else if (mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
836                 || mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
837             if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)
838                     && mHealthInfo.batteryLevel >= 100) {
839                 return com.android.internal.R.drawable.stat_sys_battery_charge;
840             } else {
841                 return com.android.internal.R.drawable.stat_sys_battery;
842             }
843         } else {
844             return com.android.internal.R.drawable.stat_sys_battery_unknown;
845         }
846     }
847 
848     class Shell extends ShellCommand {
849         @Override
onCommand(String cmd)850         public int onCommand(String cmd) {
851             return onShellCommand(this, cmd);
852         }
853 
854         @Override
onHelp()855         public void onHelp() {
856             PrintWriter pw = getOutPrintWriter();
857             dumpHelp(pw);
858         }
859     }
860 
dumpHelp(PrintWriter pw)861     static void dumpHelp(PrintWriter pw) {
862         pw.println("Battery service (battery) commands:");
863         pw.println("  help");
864         pw.println("    Print this help text.");
865         pw.println("  get [-f] [ac|usb|wireless|status|level|temp|present|counter|invalid]");
866         pw.println(
867                 "  set [-f] [ac|usb|wireless|status|level|temp|present|counter|invalid] <value>");
868         pw.println("    Force a battery property value, freezing battery state.");
869         pw.println("    -f: force a battery change broadcast be sent, prints new sequence.");
870         pw.println("  unplug [-f]");
871         pw.println("    Force battery unplugged, freezing battery state.");
872         pw.println("    -f: force a battery change broadcast be sent, prints new sequence.");
873         pw.println("  reset [-f]");
874         pw.println("    Unfreeze battery state, returning to current hardware values.");
875         pw.println("    -f: force a battery change broadcast be sent, prints new sequence.");
876         if (Build.IS_DEBUGGABLE) {
877             pw.println("  suspend_input");
878             pw.println("    Suspend charging even if plugged in. ");
879         }
880     }
881 
882     static final int OPTION_FORCE_UPDATE = 1<<0;
883 
parseOptions(Shell shell)884     int parseOptions(Shell shell) {
885         String opt;
886         int opts = 0;
887         while ((opt = shell.getNextOption()) != null) {
888             if ("-f".equals(opt)) {
889                 opts |= OPTION_FORCE_UPDATE;
890             }
891         }
892         return opts;
893     }
894 
onShellCommand(Shell shell, String cmd)895     int onShellCommand(Shell shell, String cmd) {
896         if (cmd == null) {
897             return shell.handleDefaultCommands(cmd);
898         }
899         PrintWriter pw = shell.getOutPrintWriter();
900         switch (cmd) {
901             case "unplug": {
902                 int opts = parseOptions(shell);
903                 getContext().enforceCallingOrSelfPermission(
904                         android.Manifest.permission.DEVICE_POWER, null);
905                 unplugBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
906             } break;
907             case "get": {
908                 final String key = shell.getNextArg();
909                 if (key == null) {
910                     pw.println("No property specified");
911                     return -1;
912 
913                 }
914                 switch (key) {
915                     case "present":
916                         pw.println(mHealthInfo.batteryPresent);
917                         break;
918                     case "ac":
919                         pw.println(mHealthInfo.chargerAcOnline);
920                         break;
921                     case "usb":
922                         pw.println(mHealthInfo.chargerUsbOnline);
923                         break;
924                     case "wireless":
925                         pw.println(mHealthInfo.chargerWirelessOnline);
926                         break;
927                     case "status":
928                         pw.println(mHealthInfo.batteryStatus);
929                         break;
930                     case "level":
931                         pw.println(mHealthInfo.batteryLevel);
932                         break;
933                     case "counter":
934                         pw.println(mHealthInfo.batteryChargeCounterUah);
935                         break;
936                     case "temp":
937                         pw.println(mHealthInfo.batteryTemperatureTenthsCelsius);
938                         break;
939                     case "invalid":
940                         pw.println(mInvalidCharger);
941                         break;
942                     default:
943                         pw.println("Unknown get option: " + key);
944                         break;
945                 }
946             } break;
947             case "set": {
948                 int opts = parseOptions(shell);
949                 getContext().enforceCallingOrSelfPermission(
950                         android.Manifest.permission.DEVICE_POWER, null);
951                 final String key = shell.getNextArg();
952                 if (key == null) {
953                     pw.println("No property specified");
954                     return -1;
955 
956                 }
957                 final String value = shell.getNextArg();
958                 if (value == null) {
959                     pw.println("No value specified");
960                     return -1;
961 
962                 }
963                 try {
964                     if (!mUpdatesStopped) {
965                         copyV1Battery(mLastHealthInfo, mHealthInfo);
966                     }
967                     boolean update = true;
968                     switch (key) {
969                         case "present":
970                             mHealthInfo.batteryPresent = Integer.parseInt(value) != 0;
971                             break;
972                         case "ac":
973                             mHealthInfo.chargerAcOnline = Integer.parseInt(value) != 0;
974                             break;
975                         case "usb":
976                             mHealthInfo.chargerUsbOnline = Integer.parseInt(value) != 0;
977                             break;
978                         case "wireless":
979                             mHealthInfo.chargerWirelessOnline = Integer.parseInt(value) != 0;
980                             break;
981                         case "status":
982                             mHealthInfo.batteryStatus = Integer.parseInt(value);
983                             break;
984                         case "level":
985                             mHealthInfo.batteryLevel = Integer.parseInt(value);
986                             break;
987                         case "counter":
988                             mHealthInfo.batteryChargeCounterUah = Integer.parseInt(value);
989                             break;
990                         case "temp":
991                             mHealthInfo.batteryTemperatureTenthsCelsius = Integer.parseInt(value);
992                             break;
993                         case "invalid":
994                             mInvalidCharger = Integer.parseInt(value);
995                             break;
996                         default:
997                             pw.println("Unknown set option: " + key);
998                             update = false;
999                             break;
1000                     }
1001                     if (update) {
1002                         final long ident = Binder.clearCallingIdentity();
1003                         try {
1004                             mUpdatesStopped = true;
1005                             processValuesLocked(
1006                                     /* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
1007                         } finally {
1008                             Binder.restoreCallingIdentity(ident);
1009                         }
1010                     }
1011                 } catch (NumberFormatException ex) {
1012                     pw.println("Bad value: " + value);
1013                     return -1;
1014                 }
1015             } break;
1016             case "reset": {
1017                 int opts = parseOptions(shell);
1018                 getContext().enforceCallingOrSelfPermission(
1019                         android.Manifest.permission.DEVICE_POWER, null);
1020                 resetBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
1021             } break;
1022             case "suspend_input": {
1023                 getContext().enforceCallingOrSelfPermission(
1024                         android.Manifest.permission.DEVICE_POWER, null);
1025                 suspendBatteryInput();
1026             } break;
1027             default:
1028                 return shell.handleDefaultCommands(cmd);
1029         }
1030         return 0;
1031     }
1032 
setChargerAcOnline(boolean online, boolean forceUpdate)1033     private void setChargerAcOnline(boolean online, boolean forceUpdate) {
1034         if (!mUpdatesStopped) {
1035             copyV1Battery(mLastHealthInfo, mHealthInfo);
1036         }
1037         mHealthInfo.chargerAcOnline = online;
1038         mUpdatesStopped = true;
1039         Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate));
1040     }
1041 
setBatteryLevel(int level, boolean forceUpdate)1042     private void setBatteryLevel(int level, boolean forceUpdate) {
1043         if (!mUpdatesStopped) {
1044             copyV1Battery(mLastHealthInfo, mHealthInfo);
1045         }
1046         mHealthInfo.batteryLevel = level;
1047         mUpdatesStopped = true;
1048         Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate));
1049     }
1050 
unplugBattery(boolean forceUpdate, PrintWriter pw)1051     private void unplugBattery(boolean forceUpdate, PrintWriter pw) {
1052         if (!mUpdatesStopped) {
1053             copyV1Battery(mLastHealthInfo, mHealthInfo);
1054         }
1055         mHealthInfo.chargerAcOnline = false;
1056         mHealthInfo.chargerUsbOnline = false;
1057         mHealthInfo.chargerWirelessOnline = false;
1058         mUpdatesStopped = true;
1059         Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw));
1060     }
1061 
resetBattery(boolean forceUpdate, @Nullable PrintWriter pw)1062     private void resetBattery(boolean forceUpdate, @Nullable PrintWriter pw) {
1063         if (mUpdatesStopped) {
1064             mUpdatesStopped = false;
1065             copyV1Battery(mHealthInfo, mLastHealthInfo);
1066             Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw));
1067         }
1068         if (mBatteryInputSuspended) {
1069             PowerProperties.battery_input_suspended(false);
1070             mBatteryInputSuspended = false;
1071         }
1072     }
1073 
suspendBatteryInput()1074     private void suspendBatteryInput() {
1075         if (!Build.IS_DEBUGGABLE) {
1076             throw new SecurityException(
1077                     "battery suspend_input is only supported on debuggable builds");
1078         }
1079         PowerProperties.battery_input_suspended(true);
1080         mBatteryInputSuspended = true;
1081     }
1082 
processValuesLocked(boolean forceUpdate, @Nullable PrintWriter pw)1083     private void processValuesLocked(boolean forceUpdate, @Nullable PrintWriter pw) {
1084         processValuesLocked(forceUpdate);
1085         if (pw != null && forceUpdate) {
1086             pw.println(mSequence);
1087         }
1088     }
1089 
dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args)1090     private void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
1091         synchronized (mLock) {
1092             if (args == null || args.length == 0 || "-a".equals(args[0])) {
1093                 pw.println("Current Battery Service state:");
1094                 if (mUpdatesStopped) {
1095                     pw.println("  (UPDATES STOPPED -- use 'reset' to restart)");
1096                 }
1097                 pw.println("  AC powered: " + mHealthInfo.chargerAcOnline);
1098                 pw.println("  USB powered: " + mHealthInfo.chargerUsbOnline);
1099                 pw.println("  Wireless powered: " + mHealthInfo.chargerWirelessOnline);
1100                 pw.println("  Max charging current: " + mHealthInfo.maxChargingCurrentMicroamps);
1101                 pw.println("  Max charging voltage: " + mHealthInfo.maxChargingVoltageMicrovolts);
1102                 pw.println("  Charge counter: " + mHealthInfo.batteryChargeCounterUah);
1103                 pw.println("  status: " + mHealthInfo.batteryStatus);
1104                 pw.println("  health: " + mHealthInfo.batteryHealth);
1105                 pw.println("  present: " + mHealthInfo.batteryPresent);
1106                 pw.println("  level: " + mHealthInfo.batteryLevel);
1107                 pw.println("  scale: " + BATTERY_SCALE);
1108                 pw.println("  voltage: " + mHealthInfo.batteryVoltageMillivolts);
1109                 pw.println("  temperature: " + mHealthInfo.batteryTemperatureTenthsCelsius);
1110                 pw.println("  technology: " + mHealthInfo.batteryTechnology);
1111             } else {
1112                 Shell shell = new Shell();
1113                 shell.exec(mBinderService, null, fd, null, args, null, new ResultReceiver(null));
1114             }
1115         }
1116     }
1117 
dumpProto(FileDescriptor fd)1118     private void dumpProto(FileDescriptor fd) {
1119         final ProtoOutputStream proto = new ProtoOutputStream(fd);
1120 
1121         synchronized (mLock) {
1122             proto.write(BatteryServiceDumpProto.ARE_UPDATES_STOPPED, mUpdatesStopped);
1123             int batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_NONE;
1124             if (mHealthInfo.chargerAcOnline) {
1125                 batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_AC;
1126             } else if (mHealthInfo.chargerUsbOnline) {
1127                 batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_USB;
1128             } else if (mHealthInfo.chargerWirelessOnline) {
1129                 batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_WIRELESS;
1130             } else if (mHealthInfo.chargerDockOnline) {
1131                 batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_DOCK;
1132             }
1133             proto.write(BatteryServiceDumpProto.PLUGGED, batteryPluggedValue);
1134             proto.write(
1135                     BatteryServiceDumpProto.MAX_CHARGING_CURRENT,
1136                     mHealthInfo.maxChargingCurrentMicroamps);
1137             proto.write(
1138                     BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE,
1139                     mHealthInfo.maxChargingVoltageMicrovolts);
1140             proto.write(
1141                     BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah);
1142             proto.write(BatteryServiceDumpProto.STATUS, mHealthInfo.batteryStatus);
1143             proto.write(BatteryServiceDumpProto.HEALTH, mHealthInfo.batteryHealth);
1144             proto.write(BatteryServiceDumpProto.IS_PRESENT, mHealthInfo.batteryPresent);
1145             proto.write(BatteryServiceDumpProto.LEVEL, mHealthInfo.batteryLevel);
1146             proto.write(BatteryServiceDumpProto.SCALE, BATTERY_SCALE);
1147             proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.batteryVoltageMillivolts);
1148             proto.write(
1149                     BatteryServiceDumpProto.TEMPERATURE,
1150                     mHealthInfo.batteryTemperatureTenthsCelsius);
1151             proto.write(BatteryServiceDumpProto.TECHNOLOGY, mHealthInfo.batteryTechnology);
1152         }
1153         proto.flush();
1154     }
1155 
traceBegin(String name)1156     private static void traceBegin(String name) {
1157         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
1158     }
1159 
traceEnd()1160     private static void traceEnd() {
1161         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
1162     }
1163 
1164     private final class Led {
1165         private final LogicalLight mBatteryLight;
1166 
1167         private final int mBatteryLowARGB;
1168         private final int mBatteryMediumARGB;
1169         private final int mBatteryFullARGB;
1170         private final int mBatteryLedOn;
1171         private final int mBatteryLedOff;
1172 
Led(Context context, LightsManager lights)1173         public Led(Context context, LightsManager lights) {
1174             mBatteryLight = lights.getLight(LightsManager.LIGHT_ID_BATTERY);
1175 
1176             mBatteryLowARGB = context.getResources().getInteger(
1177                     com.android.internal.R.integer.config_notificationsBatteryLowARGB);
1178             mBatteryMediumARGB = context.getResources().getInteger(
1179                     com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
1180             mBatteryFullARGB = context.getResources().getInteger(
1181                     com.android.internal.R.integer.config_notificationsBatteryFullARGB);
1182             mBatteryLedOn = context.getResources().getInteger(
1183                     com.android.internal.R.integer.config_notificationsBatteryLedOn);
1184             mBatteryLedOff = context.getResources().getInteger(
1185                     com.android.internal.R.integer.config_notificationsBatteryLedOff);
1186         }
1187 
1188         /**
1189          * Synchronize on BatteryService.
1190          */
updateLightsLocked()1191         public void updateLightsLocked() {
1192             if (mBatteryLight == null) {
1193                 return;
1194             }
1195             final int level = mHealthInfo.batteryLevel;
1196             final int status = mHealthInfo.batteryStatus;
1197             if (level < mLowBatteryWarningLevel) {
1198                 if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
1199                     // Solid red when battery is charging
1200                     mBatteryLight.setColor(mBatteryLowARGB);
1201                 } else {
1202                     // Flash red when battery is low and not charging
1203                     mBatteryLight.setFlashing(mBatteryLowARGB, LogicalLight.LIGHT_FLASH_TIMED,
1204                             mBatteryLedOn, mBatteryLedOff);
1205                 }
1206             } else if (status == BatteryManager.BATTERY_STATUS_CHARGING
1207                     || status == BatteryManager.BATTERY_STATUS_FULL) {
1208                 if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
1209                     // Solid green when full or charging and nearly full
1210                     mBatteryLight.setColor(mBatteryFullARGB);
1211                 } else {
1212                     // Solid orange when charging and halfway full
1213                     mBatteryLight.setColor(mBatteryMediumARGB);
1214                 }
1215             } else {
1216                 // No lights if not charging and not low
1217                 mBatteryLight.turnOff();
1218             }
1219         }
1220     }
1221 
1222     private final class BinderService extends Binder {
dump(FileDescriptor fd, PrintWriter pw, String[] args)1223         @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1224             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
1225 
1226             if (args.length > 0 && "--proto".equals(args[0])) {
1227                 dumpProto(fd);
1228             } else {
1229                 dumpInternal(fd, pw, args);
1230             }
1231         }
1232 
onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)1233         @Override public void onShellCommand(FileDescriptor in, FileDescriptor out,
1234                 FileDescriptor err, String[] args, ShellCallback callback,
1235                 ResultReceiver resultReceiver) {
1236             (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
1237         }
1238     }
1239 
1240     // Reduced IBatteryPropertiesRegistrar that only implements getProperty for usage
1241     // in BatteryManager.
1242     private final class BatteryPropertiesRegistrar extends IBatteryPropertiesRegistrar.Stub {
1243         @Override
getProperty(int id, final BatteryProperty prop)1244         public int getProperty(int id, final BatteryProperty prop) throws RemoteException {
1245             return mHealthServiceWrapper.getProperty(id, prop);
1246         }
1247         @Override
scheduleUpdate()1248         public void scheduleUpdate() throws RemoteException {
1249             mHealthServiceWrapper.scheduleUpdate();
1250         }
1251     }
1252 
1253     private final class LocalService extends BatteryManagerInternal {
1254         @Override
isPowered(int plugTypeSet)1255         public boolean isPowered(int plugTypeSet) {
1256             synchronized (mLock) {
1257                 return isPoweredLocked(plugTypeSet);
1258             }
1259         }
1260 
1261         @Override
getPlugType()1262         public int getPlugType() {
1263             synchronized (mLock) {
1264                 return mPlugType;
1265             }
1266         }
1267 
1268         @Override
getBatteryLevel()1269         public int getBatteryLevel() {
1270             synchronized (mLock) {
1271                 return mHealthInfo.batteryLevel;
1272             }
1273         }
1274 
1275         @Override
getBatteryChargeCounter()1276         public int getBatteryChargeCounter() {
1277             synchronized (mLock) {
1278                 return mHealthInfo.batteryChargeCounterUah;
1279             }
1280         }
1281 
1282         @Override
getBatteryFullCharge()1283         public int getBatteryFullCharge() {
1284             synchronized (mLock) {
1285                 return mHealthInfo.batteryFullChargeUah;
1286             }
1287         }
1288 
1289         @Override
getBatteryHealth()1290         public int getBatteryHealth() {
1291             synchronized (mLock) {
1292                 return mHealthInfo.batteryHealth;
1293             }
1294         }
1295 
1296         @Override
getBatteryLevelLow()1297         public boolean getBatteryLevelLow() {
1298             synchronized (mLock) {
1299                 return mBatteryLevelLow;
1300             }
1301         }
1302 
1303         @Override
getInvalidCharger()1304         public int getInvalidCharger() {
1305             synchronized (mLock) {
1306                 return mInvalidCharger;
1307             }
1308         }
1309 
1310         @Override
setChargerAcOnline(boolean online, boolean forceUpdate)1311         public void setChargerAcOnline(boolean online, boolean forceUpdate) {
1312             getContext().enforceCallingOrSelfPermission(
1313                     android.Manifest.permission.DEVICE_POWER, /* message= */ null);
1314             BatteryService.this.setChargerAcOnline(online, forceUpdate);
1315         }
1316 
1317         @Override
setBatteryLevel(int level, boolean forceUpdate)1318         public void setBatteryLevel(int level, boolean forceUpdate) {
1319             getContext().enforceCallingOrSelfPermission(
1320                     android.Manifest.permission.DEVICE_POWER, /* message= */ null);
1321             BatteryService.this.setBatteryLevel(level, forceUpdate);
1322         }
1323 
1324         @Override
unplugBattery(boolean forceUpdate)1325         public void unplugBattery(boolean forceUpdate) {
1326             getContext().enforceCallingOrSelfPermission(
1327                     android.Manifest.permission.DEVICE_POWER, /* message= */ null);
1328             BatteryService.this.unplugBattery(forceUpdate, /* printWriter= */ null);
1329         }
1330 
1331         @Override
resetBattery(boolean forceUpdate)1332         public void resetBattery(boolean forceUpdate) {
1333             getContext().enforceCallingOrSelfPermission(
1334                     android.Manifest.permission.DEVICE_POWER, /* message= */ null);
1335             BatteryService.this.resetBattery(forceUpdate, /* printWriter= */ null);
1336         }
1337 
1338         @Override
suspendBatteryInput()1339         public void suspendBatteryInput() {
1340             getContext().enforceCallingOrSelfPermission(
1341                     android.Manifest.permission.DEVICE_POWER, /* message= */ null);
1342             BatteryService.this.suspendBatteryInput();
1343         }
1344     }
1345 }
1346