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