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