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