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