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