1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server; 18 19 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent; 20 21 import android.app.ActivityManager; 22 import android.app.ActivityManagerInternal; 23 import android.content.ContentResolver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.database.ContentObserver; 27 import android.hardware.health.V1_0.HealthInfo; 28 import android.hardware.health.V2_0.IHealth; 29 import android.hardware.health.V2_0.Result; 30 import android.hardware.health.V2_1.BatteryCapacityLevel; 31 import android.hardware.health.V2_1.Constants; 32 import android.hardware.health.V2_1.IHealthInfoCallback; 33 import android.hidl.manager.V1_0.IServiceManager; 34 import android.hidl.manager.V1_0.IServiceNotification; 35 import android.metrics.LogMaker; 36 import android.os.BatteryManager; 37 import android.os.BatteryManagerInternal; 38 import android.os.BatteryProperty; 39 import android.os.BatteryStats; 40 import android.os.Binder; 41 import android.os.Bundle; 42 import android.os.DropBoxManager; 43 import android.os.FileUtils; 44 import android.os.Handler; 45 import android.os.HandlerThread; 46 import android.os.IBatteryPropertiesRegistrar; 47 import android.os.IBinder; 48 import android.os.OsProtoEnums; 49 import android.os.PowerManager; 50 import android.os.RemoteException; 51 import android.os.ResultReceiver; 52 import android.os.ServiceManager; 53 import android.os.ShellCallback; 54 import android.os.ShellCommand; 55 import android.os.SystemClock; 56 import android.os.Trace; 57 import android.os.UEventObserver; 58 import android.os.UserHandle; 59 import android.provider.Settings; 60 import android.service.battery.BatteryServiceDumpProto; 61 import android.util.EventLog; 62 import android.util.MutableInt; 63 import android.util.Slog; 64 import android.util.proto.ProtoOutputStream; 65 66 import com.android.internal.annotations.VisibleForTesting; 67 import com.android.internal.app.IBatteryStats; 68 import com.android.internal.logging.MetricsLogger; 69 import com.android.internal.util.DumpUtils; 70 import com.android.server.am.BatteryStatsService; 71 import com.android.server.lights.LightsManager; 72 import com.android.server.lights.LogicalLight; 73 74 import java.io.File; 75 import java.io.FileDescriptor; 76 import java.io.FileOutputStream; 77 import java.io.IOException; 78 import java.io.PrintWriter; 79 import java.util.ArrayDeque; 80 import java.util.ArrayList; 81 import java.util.Arrays; 82 import java.util.List; 83 import java.util.NoSuchElementException; 84 import java.util.Objects; 85 import java.util.concurrent.atomic.AtomicReference; 86 87 /** 88 * <p>BatteryService monitors the charging status, and charge level of the device 89 * battery. When these values change this service broadcasts the new values 90 * to all {@link android.content.BroadcastReceiver IntentReceivers} that are 91 * watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED 92 * BATTERY_CHANGED} action.</p> 93 * <p>The new values are stored in the Intent data and can be retrieved by 94 * calling {@link android.content.Intent#getExtra Intent.getExtra} with the 95 * following keys:</p> 96 * <p>"scale" - int, the maximum value for the charge level</p> 97 * <p>"level" - int, charge level, from 0 through "scale" inclusive</p> 98 * <p>"status" - String, the current charging status.<br /> 99 * <p>"health" - String, the current battery health.<br /> 100 * <p>"present" - boolean, true if the battery is present<br /> 101 * <p>"icon-small" - int, suggested small icon to use for this state</p> 102 * <p>"plugged" - int, 0 if the device is not plugged in; 1 if plugged 103 * into an AC power adapter; 2 if plugged in via USB.</p> 104 * <p>"voltage" - int, current battery voltage in millivolts</p> 105 * <p>"temperature" - int, current battery temperature in tenths of 106 * a degree Centigrade</p> 107 * <p>"technology" - String, the type of battery installed, e.g. "Li-ion"</p> 108 * 109 * <p> 110 * The battery service may be called by the power manager while holding its locks so 111 * we take care to post all outcalls into the activity manager to a handler. 112 * 113 * FIXME: Ideally the power manager would perform all of its calls into the battery 114 * service asynchronously itself. 115 * </p> 116 */ 117 public final class BatteryService extends SystemService { 118 private static final String TAG = BatteryService.class.getSimpleName(); 119 120 private static final boolean DEBUG = false; 121 122 private static final int BATTERY_SCALE = 100; // battery capacity is a percentage 123 124 private static final long HEALTH_HAL_WAIT_MS = 1000; 125 private static final long BATTERY_LEVEL_CHANGE_THROTTLE_MS = 60_000; 126 private static final int MAX_BATTERY_LEVELS_QUEUE_SIZE = 100; 127 128 // Used locally for determining when to make a last ditch effort to log 129 // discharge stats before the device dies. 130 private int mCriticalBatteryLevel; 131 132 // TODO: Current args don't work since "--unplugged" flag was purposefully removed. 133 private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" }; 134 135 private static final String DUMPSYS_DATA_PATH = "/data/system/"; 136 137 // This should probably be exposed in the API, though it's not critical 138 private static final int BATTERY_PLUGGED_NONE = OsProtoEnums.BATTERY_PLUGGED_NONE; // = 0 139 140 private final Context mContext; 141 private final IBatteryStats mBatteryStats; 142 BinderService mBinderService; 143 private final Handler mHandler; 144 145 private final Object mLock = new Object(); 146 147 private HealthInfo mHealthInfo; 148 private final HealthInfo mLastHealthInfo = new HealthInfo(); 149 private android.hardware.health.V2_1.HealthInfo mHealthInfo2p1; 150 private boolean mBatteryLevelCritical; 151 private int mLastBatteryStatus; 152 private int mLastBatteryHealth; 153 private boolean mLastBatteryPresent; 154 private int mLastBatteryLevel; 155 private int mLastBatteryVoltage; 156 private int mLastBatteryTemperature; 157 private boolean mLastBatteryLevelCritical; 158 private int mLastMaxChargingCurrent; 159 private int mLastMaxChargingVoltage; 160 private int mLastChargeCounter; 161 162 private int mSequence = 1; 163 164 private int mInvalidCharger; 165 private int mLastInvalidCharger; 166 167 private int mLowBatteryWarningLevel; 168 private int mLastLowBatteryWarningLevel; 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 mLastLowBatteryWarningLevel = mLowBatteryWarningLevel; 318 mLowBatteryWarningLevel = Settings.Global.getInt(resolver, 319 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel); 320 if (mLowBatteryWarningLevel == 0) { 321 mLowBatteryWarningLevel = defWarnLevel; 322 } 323 if (mLowBatteryWarningLevel < mCriticalBatteryLevel) { 324 mLowBatteryWarningLevel = mCriticalBatteryLevel; 325 } 326 mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger( 327 com.android.internal.R.integer.config_lowBatteryCloseWarningBump); 328 processValuesLocked(true); 329 } 330 isPoweredLocked(int plugTypeSet)331 private boolean isPoweredLocked(int plugTypeSet) { 332 // assume we are powered if battery state is unknown so 333 // the "stay on while plugged in" option will work. 334 if (mHealthInfo.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) { 335 return true; 336 } 337 if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mHealthInfo.chargerAcOnline) { 338 return true; 339 } 340 if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mHealthInfo.chargerUsbOnline) { 341 return true; 342 } 343 if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mHealthInfo.chargerWirelessOnline) { 344 return true; 345 } 346 return false; 347 } 348 shouldSendBatteryLowLocked()349 private boolean shouldSendBatteryLowLocked() { 350 final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE; 351 final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE; 352 353 /* The ACTION_BATTERY_LOW broadcast is sent in these situations: 354 * - is just un-plugged (previously was plugged) and battery level is 355 * less than or equal to WARNING, or 356 * - is not plugged and battery level falls to WARNING boundary 357 * (becomes <= mLowBatteryWarningLevel). 358 */ 359 return !plugged 360 && mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN 361 && mHealthInfo.batteryLevel <= mLowBatteryWarningLevel 362 && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel 363 || mHealthInfo.batteryLevel > mLastLowBatteryWarningLevel); 364 } 365 shouldShutdownLocked()366 private boolean shouldShutdownLocked() { 367 if (mHealthInfo2p1.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) { 368 return (mHealthInfo2p1.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL); 369 } 370 if (mHealthInfo.batteryLevel > 0) { 371 return false; 372 } 373 374 // Battery-less devices should not shutdown. 375 if (!mHealthInfo.batteryPresent) { 376 return false; 377 } 378 379 // If battery state is not CHARGING, shutdown. 380 // - If battery present and state == unknown, this is an unexpected error state. 381 // - If level <= 0 and state == full, this is also an unexpected state 382 // - All other states (NOT_CHARGING, DISCHARGING) means it is not charging. 383 return mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_CHARGING; 384 } 385 shutdownIfNoPowerLocked()386 private void shutdownIfNoPowerLocked() { 387 // shut down gracefully if our battery is critically low and we are not powered. 388 // wait until the system has booted before attempting to display the shutdown dialog. 389 if (shouldShutdownLocked()) { 390 mHandler.post(new Runnable() { 391 @Override 392 public void run() { 393 if (mActivityManagerInternal.isSystemReady()) { 394 Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); 395 intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); 396 intent.putExtra(Intent.EXTRA_REASON, 397 PowerManager.SHUTDOWN_LOW_BATTERY); 398 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 399 mContext.startActivityAsUser(intent, UserHandle.CURRENT); 400 } 401 } 402 }); 403 } 404 } 405 shutdownIfOverTempLocked()406 private void shutdownIfOverTempLocked() { 407 // shut down gracefully if temperature is too high (> 68.0C by default) 408 // wait until the system has booted before attempting to display the 409 // shutdown dialog. 410 if (mHealthInfo.batteryTemperature > mShutdownBatteryTemperature) { 411 mHandler.post(new Runnable() { 412 @Override 413 public void run() { 414 if (mActivityManagerInternal.isSystemReady()) { 415 Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); 416 intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); 417 intent.putExtra(Intent.EXTRA_REASON, 418 PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE); 419 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 420 mContext.startActivityAsUser(intent, UserHandle.CURRENT); 421 } 422 } 423 }); 424 } 425 } 426 update(android.hardware.health.V2_1.HealthInfo info)427 private void update(android.hardware.health.V2_1.HealthInfo info) { 428 traceBegin("HealthInfoUpdate"); 429 430 Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryChargeCounter", 431 info.legacy.legacy.batteryChargeCounter); 432 Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent", 433 info.legacy.legacy.batteryCurrent); 434 435 synchronized (mLock) { 436 if (!mUpdatesStopped) { 437 mHealthInfo = info.legacy.legacy; 438 mHealthInfo2p1 = info; 439 // Process the new values. 440 processValuesLocked(false); 441 mLock.notifyAll(); // for any waiters on new info 442 } else { 443 copy(mLastHealthInfo, info.legacy.legacy); 444 } 445 } 446 traceEnd(); 447 } 448 copy(HealthInfo dst, HealthInfo src)449 private static void copy(HealthInfo dst, HealthInfo src) { 450 dst.chargerAcOnline = src.chargerAcOnline; 451 dst.chargerUsbOnline = src.chargerUsbOnline; 452 dst.chargerWirelessOnline = src.chargerWirelessOnline; 453 dst.maxChargingCurrent = src.maxChargingCurrent; 454 dst.maxChargingVoltage = src.maxChargingVoltage; 455 dst.batteryStatus = src.batteryStatus; 456 dst.batteryHealth = src.batteryHealth; 457 dst.batteryPresent = src.batteryPresent; 458 dst.batteryLevel = src.batteryLevel; 459 dst.batteryVoltage = src.batteryVoltage; 460 dst.batteryTemperature = src.batteryTemperature; 461 dst.batteryCurrent = src.batteryCurrent; 462 dst.batteryCycleCount = src.batteryCycleCount; 463 dst.batteryFullCharge = src.batteryFullCharge; 464 dst.batteryChargeCounter = src.batteryChargeCounter; 465 dst.batteryTechnology = src.batteryTechnology; 466 } 467 processValuesLocked(boolean force)468 private void processValuesLocked(boolean force) { 469 boolean logOutlier = false; 470 long dischargeDuration = 0; 471 472 mBatteryLevelCritical = 473 mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN 474 && mHealthInfo.batteryLevel <= mCriticalBatteryLevel; 475 if (mHealthInfo.chargerAcOnline) { 476 mPlugType = BatteryManager.BATTERY_PLUGGED_AC; 477 } else if (mHealthInfo.chargerUsbOnline) { 478 mPlugType = BatteryManager.BATTERY_PLUGGED_USB; 479 } else if (mHealthInfo.chargerWirelessOnline) { 480 mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS; 481 } else { 482 mPlugType = BATTERY_PLUGGED_NONE; 483 } 484 485 if (DEBUG) { 486 Slog.d(TAG, "Processing new values: " 487 + "info=" + mHealthInfo 488 + ", mBatteryLevelCritical=" + mBatteryLevelCritical 489 + ", mPlugType=" + mPlugType); 490 } 491 492 // Let the battery stats keep track of the current level. 493 try { 494 mBatteryStats.setBatteryState(mHealthInfo.batteryStatus, mHealthInfo.batteryHealth, 495 mPlugType, mHealthInfo.batteryLevel, mHealthInfo.batteryTemperature, 496 mHealthInfo.batteryVoltage, mHealthInfo.batteryChargeCounter, 497 mHealthInfo.batteryFullCharge, 498 mHealthInfo2p1.batteryChargeTimeToFullNowSeconds); 499 } catch (RemoteException e) { 500 // Should never happen. 501 } 502 503 shutdownIfNoPowerLocked(); 504 shutdownIfOverTempLocked(); 505 506 if (force || (mHealthInfo.batteryStatus != mLastBatteryStatus || 507 mHealthInfo.batteryHealth != mLastBatteryHealth || 508 mHealthInfo.batteryPresent != mLastBatteryPresent || 509 mHealthInfo.batteryLevel != mLastBatteryLevel || 510 mPlugType != mLastPlugType || 511 mHealthInfo.batteryVoltage != mLastBatteryVoltage || 512 mHealthInfo.batteryTemperature != mLastBatteryTemperature || 513 mHealthInfo.maxChargingCurrent != mLastMaxChargingCurrent || 514 mHealthInfo.maxChargingVoltage != mLastMaxChargingVoltage || 515 mHealthInfo.batteryChargeCounter != mLastChargeCounter || 516 mInvalidCharger != mLastInvalidCharger)) { 517 518 if (mPlugType != mLastPlugType) { 519 if (mLastPlugType == BATTERY_PLUGGED_NONE) { 520 // discharging -> charging 521 mChargeStartLevel = mHealthInfo.batteryLevel; 522 mChargeStartTime = SystemClock.elapsedRealtime(); 523 524 final LogMaker builder = new LogMaker(MetricsEvent.ACTION_CHARGE); 525 builder.setType(MetricsEvent.TYPE_ACTION); 526 builder.addTaggedData(MetricsEvent.FIELD_PLUG_TYPE, mPlugType); 527 builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_START, 528 mHealthInfo.batteryLevel); 529 mMetricsLogger.write(builder); 530 531 // There's no value in this data unless we've discharged at least once and the 532 // battery level has changed; so don't log until it does. 533 if (mDischargeStartTime != 0 && mDischargeStartLevel != mHealthInfo.batteryLevel) { 534 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime; 535 logOutlier = true; 536 EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration, 537 mDischargeStartLevel, mHealthInfo.batteryLevel); 538 // make sure we see a discharge event before logging again 539 mDischargeStartTime = 0; 540 } 541 } else if (mPlugType == BATTERY_PLUGGED_NONE) { 542 // charging -> discharging or we just powered up 543 mDischargeStartTime = SystemClock.elapsedRealtime(); 544 mDischargeStartLevel = mHealthInfo.batteryLevel; 545 546 long chargeDuration = SystemClock.elapsedRealtime() - mChargeStartTime; 547 if (mChargeStartTime != 0 && chargeDuration != 0) { 548 final LogMaker builder = new LogMaker(MetricsEvent.ACTION_CHARGE); 549 builder.setType(MetricsEvent.TYPE_DISMISS); 550 builder.addTaggedData(MetricsEvent.FIELD_PLUG_TYPE, mLastPlugType); 551 builder.addTaggedData(MetricsEvent.FIELD_CHARGING_DURATION_MILLIS, 552 chargeDuration); 553 builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_START, 554 mChargeStartLevel); 555 builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_END, 556 mHealthInfo.batteryLevel); 557 mMetricsLogger.write(builder); 558 } 559 mChargeStartTime = 0; 560 } 561 } 562 if (mHealthInfo.batteryStatus != mLastBatteryStatus || 563 mHealthInfo.batteryHealth != mLastBatteryHealth || 564 mHealthInfo.batteryPresent != mLastBatteryPresent || 565 mPlugType != mLastPlugType) { 566 EventLog.writeEvent(EventLogTags.BATTERY_STATUS, 567 mHealthInfo.batteryStatus, mHealthInfo.batteryHealth, mHealthInfo.batteryPresent ? 1 : 0, 568 mPlugType, mHealthInfo.batteryTechnology); 569 } 570 if (mHealthInfo.batteryLevel != mLastBatteryLevel) { 571 // Don't do this just from voltage or temperature changes, that is 572 // too noisy. 573 EventLog.writeEvent(EventLogTags.BATTERY_LEVEL, 574 mHealthInfo.batteryLevel, mHealthInfo.batteryVoltage, mHealthInfo.batteryTemperature); 575 } 576 if (mBatteryLevelCritical && !mLastBatteryLevelCritical && 577 mPlugType == BATTERY_PLUGGED_NONE) { 578 // We want to make sure we log discharge cycle outliers 579 // if the battery is about to die. 580 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime; 581 logOutlier = true; 582 } 583 584 if (!mBatteryLevelLow) { 585 // Should we now switch in to low battery mode? 586 if (mPlugType == BATTERY_PLUGGED_NONE 587 && mHealthInfo.batteryStatus != 588 BatteryManager.BATTERY_STATUS_UNKNOWN 589 && mHealthInfo.batteryLevel <= mLowBatteryWarningLevel) { 590 mBatteryLevelLow = true; 591 } 592 } else { 593 // Should we now switch out of low battery mode? 594 if (mPlugType != BATTERY_PLUGGED_NONE) { 595 mBatteryLevelLow = false; 596 } else if (mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) { 597 mBatteryLevelLow = false; 598 } else if (force && mHealthInfo.batteryLevel >= mLowBatteryWarningLevel) { 599 // If being forced, the previous state doesn't matter, we will just 600 // absolutely check to see if we are now above the warning level. 601 mBatteryLevelLow = false; 602 } 603 } 604 605 mSequence++; 606 607 // Separate broadcast is sent for power connected / not connected 608 // since the standard intent will not wake any applications and some 609 // applications may want to have smart behavior based on this. 610 if (mPlugType != 0 && mLastPlugType == 0) { 611 final Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED); 612 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 613 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); 614 mHandler.post(new Runnable() { 615 @Override 616 public void run() { 617 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); 618 } 619 }); 620 } 621 else if (mPlugType == 0 && mLastPlugType != 0) { 622 final Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED); 623 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 624 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); 625 mHandler.post(new Runnable() { 626 @Override 627 public void run() { 628 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); 629 } 630 }); 631 } 632 633 if (shouldSendBatteryLowLocked()) { 634 mSentLowBatteryBroadcast = true; 635 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW); 636 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 637 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); 638 mHandler.post(new Runnable() { 639 @Override 640 public void run() { 641 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); 642 } 643 }); 644 } else if (mSentLowBatteryBroadcast && 645 mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) { 646 mSentLowBatteryBroadcast = false; 647 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY); 648 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 649 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); 650 mHandler.post(new Runnable() { 651 @Override 652 public void run() { 653 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); 654 } 655 }); 656 } 657 658 // We are doing this after sending the above broadcasts, so anything processing 659 // them will get the new sequence number at that point. (See for example how testing 660 // of JobScheduler's BatteryController works.) 661 sendBatteryChangedIntentLocked(); 662 if (mLastBatteryLevel != mHealthInfo.batteryLevel || mLastPlugType != mPlugType) { 663 sendBatteryLevelChangedIntentLocked(); 664 } 665 666 667 // Update the battery LED 668 mLed.updateLightsLocked(); 669 670 // This needs to be done after sendIntent() so that we get the lastest battery stats. 671 if (logOutlier && dischargeDuration != 0) { 672 logOutlierLocked(dischargeDuration); 673 } 674 675 mLastBatteryStatus = mHealthInfo.batteryStatus; 676 mLastBatteryHealth = mHealthInfo.batteryHealth; 677 mLastBatteryPresent = mHealthInfo.batteryPresent; 678 mLastBatteryLevel = mHealthInfo.batteryLevel; 679 mLastPlugType = mPlugType; 680 mLastBatteryVoltage = mHealthInfo.batteryVoltage; 681 mLastBatteryTemperature = mHealthInfo.batteryTemperature; 682 mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrent; 683 mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltage; 684 mLastChargeCounter = mHealthInfo.batteryChargeCounter; 685 mLastBatteryLevelCritical = mBatteryLevelCritical; 686 mLastInvalidCharger = mInvalidCharger; 687 } 688 } 689 sendBatteryChangedIntentLocked()690 private void sendBatteryChangedIntentLocked() { 691 // Pack up the values and broadcast them to everyone 692 final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); 693 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY 694 | Intent.FLAG_RECEIVER_REPLACE_PENDING); 695 696 int icon = getIconLocked(mHealthInfo.batteryLevel); 697 698 intent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); 699 intent.putExtra(BatteryManager.EXTRA_STATUS, mHealthInfo.batteryStatus); 700 intent.putExtra(BatteryManager.EXTRA_HEALTH, mHealthInfo.batteryHealth); 701 intent.putExtra(BatteryManager.EXTRA_PRESENT, mHealthInfo.batteryPresent); 702 intent.putExtra(BatteryManager.EXTRA_LEVEL, mHealthInfo.batteryLevel); 703 intent.putExtra(BatteryManager.EXTRA_BATTERY_LOW, mSentLowBatteryBroadcast); 704 intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE); 705 intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon); 706 intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType); 707 intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage); 708 intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperature); 709 intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mHealthInfo.batteryTechnology); 710 intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger); 711 intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent); 712 intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltage); 713 intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounter); 714 if (DEBUG) { 715 Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. scale:" + BATTERY_SCALE 716 + ", info:" + mHealthInfo.toString()); 717 } 718 719 mHandler.post(() -> ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL)); 720 } 721 sendBatteryLevelChangedIntentLocked()722 private void sendBatteryLevelChangedIntentLocked() { 723 Bundle event = new Bundle(); 724 long now = SystemClock.elapsedRealtime(); 725 event.putInt(BatteryManager.EXTRA_SEQUENCE, mSequence); 726 event.putInt(BatteryManager.EXTRA_STATUS, mHealthInfo.batteryStatus); 727 event.putInt(BatteryManager.EXTRA_HEALTH, mHealthInfo.batteryHealth); 728 event.putBoolean(BatteryManager.EXTRA_PRESENT, mHealthInfo.batteryPresent); 729 event.putInt(BatteryManager.EXTRA_LEVEL, mHealthInfo.batteryLevel); 730 event.putBoolean(BatteryManager.EXTRA_BATTERY_LOW, mSentLowBatteryBroadcast); 731 event.putInt(BatteryManager.EXTRA_SCALE, BATTERY_SCALE); 732 event.putInt(BatteryManager.EXTRA_PLUGGED, mPlugType); 733 event.putInt(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage); 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 final int level = mHealthInfo.batteryLevel; 1105 final int status = mHealthInfo.batteryStatus; 1106 if (level < mLowBatteryWarningLevel) { 1107 if (status == BatteryManager.BATTERY_STATUS_CHARGING) { 1108 // Solid red when battery is charging 1109 mBatteryLight.setColor(mBatteryLowARGB); 1110 } else { 1111 // Flash red when battery is low and not charging 1112 mBatteryLight.setFlashing(mBatteryLowARGB, LogicalLight.LIGHT_FLASH_TIMED, 1113 mBatteryLedOn, mBatteryLedOff); 1114 } 1115 } else if (status == BatteryManager.BATTERY_STATUS_CHARGING 1116 || status == BatteryManager.BATTERY_STATUS_FULL) { 1117 if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) { 1118 // Solid green when full or charging and nearly full 1119 mBatteryLight.setColor(mBatteryFullARGB); 1120 } else { 1121 // Solid orange when charging and halfway full 1122 mBatteryLight.setColor(mBatteryMediumARGB); 1123 } 1124 } else { 1125 // No lights if not charging and not low 1126 mBatteryLight.turnOff(); 1127 } 1128 } 1129 } 1130 1131 private final class HealthHalCallback extends IHealthInfoCallback.Stub 1132 implements HealthServiceWrapper.Callback { healthInfoChanged(android.hardware.health.V2_0.HealthInfo props)1133 @Override public void healthInfoChanged(android.hardware.health.V2_0.HealthInfo props) { 1134 android.hardware.health.V2_1.HealthInfo propsLatest = 1135 new android.hardware.health.V2_1.HealthInfo(); 1136 propsLatest.legacy = props; 1137 1138 propsLatest.batteryCapacityLevel = BatteryCapacityLevel.UNSUPPORTED; 1139 propsLatest.batteryChargeTimeToFullNowSeconds = 1140 Constants.BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED; 1141 1142 BatteryService.this.update(propsLatest); 1143 } 1144 healthInfoChanged_2_1(android.hardware.health.V2_1.HealthInfo props)1145 @Override public void healthInfoChanged_2_1(android.hardware.health.V2_1.HealthInfo props) { 1146 BatteryService.this.update(props); 1147 } 1148 1149 // on new service registered onRegistration(IHealth oldService, IHealth newService, String instance)1150 @Override public void onRegistration(IHealth oldService, IHealth newService, 1151 String instance) { 1152 if (newService == null) return; 1153 1154 traceBegin("HealthUnregisterCallback"); 1155 try { 1156 if (oldService != null) { 1157 int r = oldService.unregisterCallback(this); 1158 if (r != Result.SUCCESS) { 1159 Slog.w(TAG, "health: cannot unregister previous callback: " + 1160 Result.toString(r)); 1161 } 1162 } 1163 } catch (RemoteException ex) { 1164 Slog.w(TAG, "health: cannot unregister previous callback (transaction error): " 1165 + ex.getMessage()); 1166 } finally { 1167 traceEnd(); 1168 } 1169 1170 traceBegin("HealthRegisterCallback"); 1171 try { 1172 int r = newService.registerCallback(this); 1173 if (r != Result.SUCCESS) { 1174 Slog.w(TAG, "health: cannot register callback: " + Result.toString(r)); 1175 return; 1176 } 1177 // registerCallback does NOT guarantee that update is called 1178 // immediately, so request a manual update here. 1179 newService.update(); 1180 } catch (RemoteException ex) { 1181 Slog.e(TAG, "health: cannot register callback (transaction error): " 1182 + ex.getMessage()); 1183 } finally { 1184 traceEnd(); 1185 } 1186 } 1187 } 1188 1189 private final class BinderService extends Binder { dump(FileDescriptor fd, PrintWriter pw, String[] args)1190 @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1191 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 1192 1193 if (args.length > 0 && "--proto".equals(args[0])) { 1194 dumpProto(fd); 1195 } else { 1196 dumpInternal(fd, pw, args); 1197 } 1198 } 1199 onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)1200 @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, 1201 FileDescriptor err, String[] args, ShellCallback callback, 1202 ResultReceiver resultReceiver) { 1203 (new Shell()).exec(this, in, out, err, args, callback, resultReceiver); 1204 } 1205 } 1206 1207 // Reduced IBatteryPropertiesRegistrar that only implements getProperty for usage 1208 // in BatteryManager. 1209 private final class BatteryPropertiesRegistrar extends IBatteryPropertiesRegistrar.Stub { 1210 @Override getProperty(int id, final BatteryProperty prop)1211 public int getProperty(int id, final BatteryProperty prop) throws RemoteException { 1212 traceBegin("HealthGetProperty"); 1213 try { 1214 IHealth service = mHealthServiceWrapper.getLastService(); 1215 if (service == null) throw new RemoteException("no health service"); 1216 final MutableInt outResult = new MutableInt(Result.NOT_SUPPORTED); 1217 switch(id) { 1218 case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER: 1219 service.getChargeCounter((int result, int value) -> { 1220 outResult.value = result; 1221 if (result == Result.SUCCESS) prop.setLong(value); 1222 }); 1223 break; 1224 case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW: 1225 service.getCurrentNow((int result, int value) -> { 1226 outResult.value = result; 1227 if (result == Result.SUCCESS) prop.setLong(value); 1228 }); 1229 break; 1230 case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE: 1231 service.getCurrentAverage((int result, int value) -> { 1232 outResult.value = result; 1233 if (result == Result.SUCCESS) prop.setLong(value); 1234 }); 1235 break; 1236 case BatteryManager.BATTERY_PROPERTY_CAPACITY: 1237 service.getCapacity((int result, int value) -> { 1238 outResult.value = result; 1239 if (result == Result.SUCCESS) prop.setLong(value); 1240 }); 1241 break; 1242 case BatteryManager.BATTERY_PROPERTY_STATUS: 1243 service.getChargeStatus((int result, int value) -> { 1244 outResult.value = result; 1245 if (result == Result.SUCCESS) prop.setLong(value); 1246 }); 1247 break; 1248 case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER: 1249 service.getEnergyCounter((int result, long value) -> { 1250 outResult.value = result; 1251 if (result == Result.SUCCESS) prop.setLong(value); 1252 }); 1253 break; 1254 } 1255 return outResult.value; 1256 } finally { 1257 traceEnd(); 1258 } 1259 } 1260 @Override scheduleUpdate()1261 public void scheduleUpdate() throws RemoteException { 1262 mHealthServiceWrapper.getHandlerThread().getThreadHandler().post(() -> { 1263 traceBegin("HealthScheduleUpdate"); 1264 try { 1265 IHealth service = mHealthServiceWrapper.getLastService(); 1266 if (service == null) { 1267 Slog.e(TAG, "no health service"); 1268 return; 1269 } 1270 service.update(); 1271 } catch (RemoteException ex) { 1272 Slog.e(TAG, "Cannot call update on health HAL", ex); 1273 } finally { 1274 traceEnd(); 1275 } 1276 }); 1277 } 1278 } 1279 1280 private final class LocalService extends BatteryManagerInternal { 1281 @Override isPowered(int plugTypeSet)1282 public boolean isPowered(int plugTypeSet) { 1283 synchronized (mLock) { 1284 return isPoweredLocked(plugTypeSet); 1285 } 1286 } 1287 1288 @Override getPlugType()1289 public int getPlugType() { 1290 synchronized (mLock) { 1291 return mPlugType; 1292 } 1293 } 1294 1295 @Override getBatteryLevel()1296 public int getBatteryLevel() { 1297 synchronized (mLock) { 1298 return mHealthInfo.batteryLevel; 1299 } 1300 } 1301 1302 @Override getBatteryChargeCounter()1303 public int getBatteryChargeCounter() { 1304 synchronized (mLock) { 1305 return mHealthInfo.batteryChargeCounter; 1306 } 1307 } 1308 1309 @Override getBatteryFullCharge()1310 public int getBatteryFullCharge() { 1311 synchronized (mLock) { 1312 return mHealthInfo.batteryFullCharge; 1313 } 1314 } 1315 1316 @Override getBatteryLevelLow()1317 public boolean getBatteryLevelLow() { 1318 synchronized (mLock) { 1319 return mBatteryLevelLow; 1320 } 1321 } 1322 1323 @Override getInvalidCharger()1324 public int getInvalidCharger() { 1325 synchronized (mLock) { 1326 return mInvalidCharger; 1327 } 1328 } 1329 } 1330 1331 /** 1332 * HealthServiceWrapper wraps the internal IHealth service and refreshes the service when 1333 * necessary. 1334 * 1335 * On new registration of IHealth service, {@link #onRegistration onRegistration} is called and 1336 * the internal service is refreshed. 1337 * On death of an existing IHealth service, the internal service is NOT cleared to avoid 1338 * race condition between death notification and new service notification. Hence, 1339 * a caller must check for transaction errors when calling into the service. 1340 * 1341 * @hide Should only be used internally. 1342 */ 1343 @VisibleForTesting 1344 static final class HealthServiceWrapper { 1345 private static final String TAG = "HealthServiceWrapper"; 1346 public static final String INSTANCE_HEALTHD = "backup"; 1347 public static final String INSTANCE_VENDOR = "default"; 1348 // All interesting instances, sorted by priority high -> low. 1349 private static final List<String> sAllInstances = 1350 Arrays.asList(INSTANCE_VENDOR, INSTANCE_HEALTHD); 1351 1352 private final IServiceNotification mNotification = new Notification(); 1353 private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceHwbinder"); 1354 // These variables are fixed after init. 1355 private Callback mCallback; 1356 private IHealthSupplier mHealthSupplier; 1357 private String mInstanceName; 1358 1359 // Last IHealth service received. 1360 private final AtomicReference<IHealth> mLastService = new AtomicReference<>(); 1361 1362 /** 1363 * init should be called after constructor. For testing purposes, init is not called by 1364 * constructor. 1365 */ HealthServiceWrapper()1366 HealthServiceWrapper() { 1367 } 1368 getLastService()1369 IHealth getLastService() { 1370 return mLastService.get(); 1371 } 1372 1373 /** 1374 * Start monitoring registration of new IHealth services. Only instances that are in 1375 * {@code sAllInstances} and in device / framework manifest are used. This function should 1376 * only be called once. 1377 * 1378 * mCallback.onRegistration() is called synchronously (aka in init thread) before 1379 * this method returns. 1380 * 1381 * @throws RemoteException transaction error when talking to IServiceManager 1382 * @throws NoSuchElementException if one of the following cases: 1383 * - No service manager; 1384 * - none of {@code sAllInstances} are in manifests (i.e. not 1385 * available on this device), or none of these instances are available to current 1386 * process. 1387 * @throws NullPointerException when callback is null or supplier is null 1388 */ init(Callback callback, IServiceManagerSupplier managerSupplier, IHealthSupplier healthSupplier)1389 void init(Callback callback, 1390 IServiceManagerSupplier managerSupplier, 1391 IHealthSupplier healthSupplier) 1392 throws RemoteException, NoSuchElementException, NullPointerException { 1393 if (callback == null || managerSupplier == null || healthSupplier == null) 1394 throw new NullPointerException(); 1395 1396 IServiceManager manager; 1397 1398 mCallback = callback; 1399 mHealthSupplier = healthSupplier; 1400 1401 // Initialize mLastService and call callback for the first time (in init thread) 1402 IHealth newService = null; 1403 for (String name : sAllInstances) { 1404 traceBegin("HealthInitGetService_" + name); 1405 try { 1406 newService = healthSupplier.get(name); 1407 } catch (NoSuchElementException ex) { 1408 /* ignored, handled below */ 1409 } finally { 1410 traceEnd(); 1411 } 1412 if (newService != null) { 1413 mInstanceName = name; 1414 mLastService.set(newService); 1415 break; 1416 } 1417 } 1418 1419 if (mInstanceName == null || newService == null) { 1420 throw new NoSuchElementException(String.format( 1421 "No IHealth service instance among %s is available. Perhaps no permission?", 1422 sAllInstances.toString())); 1423 } 1424 mCallback.onRegistration(null, newService, mInstanceName); 1425 1426 // Register for future service registrations 1427 traceBegin("HealthInitRegisterNotification"); 1428 mHandlerThread.start(); 1429 try { 1430 managerSupplier.get().registerForNotifications( 1431 IHealth.kInterfaceName, mInstanceName, mNotification); 1432 } finally { 1433 traceEnd(); 1434 } 1435 Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + mInstanceName); 1436 } 1437 1438 @VisibleForTesting getHandlerThread()1439 HandlerThread getHandlerThread() { 1440 return mHandlerThread; 1441 } 1442 1443 interface Callback { 1444 /** 1445 * This function is invoked asynchronously when a new and related IServiceNotification 1446 * is received. 1447 * @param service the recently retrieved service from IServiceManager. 1448 * Can be a dead service before service notification of a new service is delivered. 1449 * Implementation must handle cases for {@link RemoteException}s when calling 1450 * into service. 1451 * @param instance instance name. 1452 */ onRegistration(IHealth oldService, IHealth newService, String instance)1453 void onRegistration(IHealth oldService, IHealth newService, String instance); 1454 } 1455 1456 /** 1457 * Supplier of services. 1458 * Must not return null; throw {@link NoSuchElementException} if a service is not available. 1459 */ 1460 interface IServiceManagerSupplier { get()1461 default IServiceManager get() throws NoSuchElementException, RemoteException { 1462 return IServiceManager.getService(); 1463 } 1464 } 1465 /** 1466 * Supplier of services. 1467 * Must not return null; throw {@link NoSuchElementException} if a service is not available. 1468 */ 1469 interface IHealthSupplier { get(String name)1470 default IHealth get(String name) throws NoSuchElementException, RemoteException { 1471 return IHealth.getService(name, true /* retry */); 1472 } 1473 } 1474 1475 private class Notification extends IServiceNotification.Stub { 1476 @Override onRegistration(String interfaceName, String instanceName, boolean preexisting)1477 public final void onRegistration(String interfaceName, String instanceName, 1478 boolean preexisting) { 1479 if (!IHealth.kInterfaceName.equals(interfaceName)) return; 1480 if (!mInstanceName.equals(instanceName)) return; 1481 1482 // This runnable only runs on mHandlerThread and ordering is ensured, hence 1483 // no locking is needed inside the runnable. 1484 mHandlerThread.getThreadHandler().post(new Runnable() { 1485 @Override 1486 public void run() { 1487 try { 1488 IHealth newService = mHealthSupplier.get(mInstanceName); 1489 IHealth oldService = mLastService.getAndSet(newService); 1490 1491 // preexisting may be inaccurate (race). Check for equality here. 1492 if (Objects.equals(newService, oldService)) return; 1493 1494 Slog.i(TAG, "health: new instance registered " + mInstanceName); 1495 mCallback.onRegistration(oldService, newService, mInstanceName); 1496 } catch (NoSuchElementException | RemoteException ex) { 1497 Slog.e(TAG, "health: Cannot get instance '" + mInstanceName 1498 + "': " + ex.getMessage() + ". Perhaps no permission?"); 1499 } 1500 } 1501 }); 1502 } 1503 } 1504 } 1505 } 1506