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