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 android.app.ActivityManagerInternal; 20 import android.database.ContentObserver; 21 import android.os.BatteryStats; 22 23 import android.os.ResultReceiver; 24 import android.os.ShellCallback; 25 import android.os.ShellCommand; 26 import com.android.internal.app.IBatteryStats; 27 import com.android.internal.util.DumpUtils; 28 import com.android.server.am.BatteryStatsService; 29 import com.android.server.lights.Light; 30 import com.android.server.lights.LightsManager; 31 32 import android.app.ActivityManager; 33 import android.content.ContentResolver; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.pm.PackageManager; 37 import android.os.BatteryManager; 38 import android.os.BatteryManagerInternal; 39 import android.os.BatteryProperties; 40 import android.os.Binder; 41 import android.os.FileUtils; 42 import android.os.Handler; 43 import android.os.IBatteryPropertiesListener; 44 import android.os.IBatteryPropertiesRegistrar; 45 import android.os.IBinder; 46 import android.os.DropBoxManager; 47 import android.os.RemoteException; 48 import android.os.ServiceManager; 49 import android.os.SystemClock; 50 import android.os.UEventObserver; 51 import android.os.UserHandle; 52 import android.provider.Settings; 53 import android.service.battery.BatteryServiceDumpProto; 54 import android.util.EventLog; 55 import android.util.Slog; 56 import android.util.proto.ProtoOutputStream; 57 58 import java.io.File; 59 import java.io.FileDescriptor; 60 import java.io.FileOutputStream; 61 import java.io.IOException; 62 import java.io.PrintWriter; 63 64 65 /** 66 * <p>BatteryService monitors the charging status, and charge level of the device 67 * battery. When these values change this service broadcasts the new values 68 * to all {@link android.content.BroadcastReceiver IntentReceivers} that are 69 * watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED 70 * BATTERY_CHANGED} action.</p> 71 * <p>The new values are stored in the Intent data and can be retrieved by 72 * calling {@link android.content.Intent#getExtra Intent.getExtra} with the 73 * following keys:</p> 74 * <p>"scale" - int, the maximum value for the charge level</p> 75 * <p>"level" - int, charge level, from 0 through "scale" inclusive</p> 76 * <p>"status" - String, the current charging status.<br /> 77 * <p>"health" - String, the current battery health.<br /> 78 * <p>"present" - boolean, true if the battery is present<br /> 79 * <p>"icon-small" - int, suggested small icon to use for this state</p> 80 * <p>"plugged" - int, 0 if the device is not plugged in; 1 if plugged 81 * into an AC power adapter; 2 if plugged in via USB.</p> 82 * <p>"voltage" - int, current battery voltage in millivolts</p> 83 * <p>"temperature" - int, current battery temperature in tenths of 84 * a degree Centigrade</p> 85 * <p>"technology" - String, the type of battery installed, e.g. "Li-ion"</p> 86 * 87 * <p> 88 * The battery service may be called by the power manager while holding its locks so 89 * we take care to post all outcalls into the activity manager to a handler. 90 * 91 * FIXME: Ideally the power manager would perform all of its calls into the battery 92 * service asynchronously itself. 93 * </p> 94 */ 95 public final class BatteryService extends SystemService { 96 private static final String TAG = BatteryService.class.getSimpleName(); 97 98 private static final boolean DEBUG = false; 99 100 private static final int BATTERY_SCALE = 100; // battery capacity is a percentage 101 102 // Used locally for determining when to make a last ditch effort to log 103 // discharge stats before the device dies. 104 private int mCriticalBatteryLevel; 105 106 private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" }; 107 108 private static final String DUMPSYS_DATA_PATH = "/data/system/"; 109 110 // This should probably be exposed in the API, though it's not critical 111 private static final int BATTERY_PLUGGED_NONE = 0; 112 113 private final Context mContext; 114 private final IBatteryStats mBatteryStats; 115 BinderService mBinderService; 116 private final Handler mHandler; 117 118 private final Object mLock = new Object(); 119 120 private BatteryProperties mBatteryProps; 121 private final BatteryProperties mLastBatteryProps = new BatteryProperties(); 122 private boolean mBatteryLevelCritical; 123 private int mLastBatteryStatus; 124 private int mLastBatteryHealth; 125 private boolean mLastBatteryPresent; 126 private int mLastBatteryLevel; 127 private int mLastBatteryVoltage; 128 private int mLastBatteryTemperature; 129 private boolean mLastBatteryLevelCritical; 130 private int mLastMaxChargingCurrent; 131 private int mLastMaxChargingVoltage; 132 private int mLastChargeCounter; 133 134 private int mSequence = 1; 135 136 private int mInvalidCharger; 137 private int mLastInvalidCharger; 138 139 private int mLowBatteryWarningLevel; 140 private int mLowBatteryCloseWarningLevel; 141 private int mShutdownBatteryTemperature; 142 143 private int mPlugType; 144 private int mLastPlugType = -1; // Extra state so we can detect first run 145 146 private boolean mBatteryLevelLow; 147 148 private long mDischargeStartTime; 149 private int mDischargeStartLevel; 150 151 private boolean mUpdatesStopped; 152 153 private Led mLed; 154 155 private boolean mSentLowBatteryBroadcast = false; 156 157 private ActivityManagerInternal mActivityManagerInternal; 158 159 public BatteryService(Context context) { 160 super(context); 161 162 mContext = context; 163 mHandler = new Handler(true /*async*/); 164 mLed = new Led(context, getLocalService(LightsManager.class)); 165 mBatteryStats = BatteryStatsService.getService(); 166 mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); 167 168 mCriticalBatteryLevel = mContext.getResources().getInteger( 169 com.android.internal.R.integer.config_criticalBatteryWarningLevel); 170 mLowBatteryWarningLevel = mContext.getResources().getInteger( 171 com.android.internal.R.integer.config_lowBatteryWarningLevel); 172 mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger( 173 com.android.internal.R.integer.config_lowBatteryCloseWarningBump); 174 mShutdownBatteryTemperature = mContext.getResources().getInteger( 175 com.android.internal.R.integer.config_shutdownBatteryTemperature); 176 177 // watch for invalid charger messages if the invalid_charger switch exists 178 if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) { 179 UEventObserver invalidChargerObserver = new UEventObserver() { 180 @Override 181 public void onUEvent(UEvent event) { 182 final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0; 183 synchronized (mLock) { 184 if (mInvalidCharger != invalidCharger) { 185 mInvalidCharger = invalidCharger; 186 } 187 } 188 } 189 }; 190 invalidChargerObserver.startObserving( 191 "DEVPATH=/devices/virtual/switch/invalid_charger"); 192 } 193 } 194 195 @Override 196 public void onStart() { 197 IBinder b = ServiceManager.getService("batteryproperties"); 198 final IBatteryPropertiesRegistrar batteryPropertiesRegistrar = 199 IBatteryPropertiesRegistrar.Stub.asInterface(b); 200 try { 201 batteryPropertiesRegistrar.registerListener(new BatteryListener()); 202 } catch (RemoteException e) { 203 // Should never happen. 204 } 205 206 mBinderService = new BinderService(); 207 publishBinderService("battery", mBinderService); 208 publishLocalService(BatteryManagerInternal.class, new LocalService()); 209 } 210 211 @Override 212 public void onBootPhase(int phase) { 213 if (phase == PHASE_ACTIVITY_MANAGER_READY) { 214 // check our power situation now that it is safe to display the shutdown dialog. 215 synchronized (mLock) { 216 ContentObserver obs = new ContentObserver(mHandler) { 217 @Override 218 public void onChange(boolean selfChange) { 219 synchronized (mLock) { 220 updateBatteryWarningLevelLocked(); 221 } 222 } 223 }; 224 final ContentResolver resolver = mContext.getContentResolver(); 225 resolver.registerContentObserver(Settings.Global.getUriFor( 226 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL), 227 false, obs, UserHandle.USER_ALL); 228 updateBatteryWarningLevelLocked(); 229 } 230 } 231 } 232 233 private void updateBatteryWarningLevelLocked() { 234 final ContentResolver resolver = mContext.getContentResolver(); 235 int defWarnLevel = mContext.getResources().getInteger( 236 com.android.internal.R.integer.config_lowBatteryWarningLevel); 237 mLowBatteryWarningLevel = Settings.Global.getInt(resolver, 238 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel); 239 if (mLowBatteryWarningLevel == 0) { 240 mLowBatteryWarningLevel = defWarnLevel; 241 } 242 if (mLowBatteryWarningLevel < mCriticalBatteryLevel) { 243 mLowBatteryWarningLevel = mCriticalBatteryLevel; 244 } 245 mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger( 246 com.android.internal.R.integer.config_lowBatteryCloseWarningBump); 247 processValuesLocked(true); 248 } 249 250 private boolean isPoweredLocked(int plugTypeSet) { 251 // assume we are powered if battery state is unknown so 252 // the "stay on while plugged in" option will work. 253 if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) { 254 return true; 255 } 256 if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mBatteryProps.chargerAcOnline) { 257 return true; 258 } 259 if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mBatteryProps.chargerUsbOnline) { 260 return true; 261 } 262 if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mBatteryProps.chargerWirelessOnline) { 263 return true; 264 } 265 return false; 266 } 267 268 private boolean shouldSendBatteryLowLocked() { 269 final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE; 270 final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE; 271 272 /* The ACTION_BATTERY_LOW broadcast is sent in these situations: 273 * - is just un-plugged (previously was plugged) and battery level is 274 * less than or equal to WARNING, or 275 * - is not plugged and battery level falls to WARNING boundary 276 * (becomes <= mLowBatteryWarningLevel). 277 */ 278 return !plugged 279 && mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN 280 && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel 281 && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel); 282 } 283 284 private void shutdownIfNoPowerLocked() { 285 // shut down gracefully if our battery is critically low and we are not powered. 286 // wait until the system has booted before attempting to display the shutdown dialog. 287 if (mBatteryProps.batteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) { 288 mHandler.post(new Runnable() { 289 @Override 290 public void run() { 291 if (mActivityManagerInternal.isSystemReady()) { 292 Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); 293 intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); 294 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 295 mContext.startActivityAsUser(intent, UserHandle.CURRENT); 296 } 297 } 298 }); 299 } 300 } 301 302 private void shutdownIfOverTempLocked() { 303 // shut down gracefully if temperature is too high (> 68.0C by default) 304 // wait until the system has booted before attempting to display the 305 // shutdown dialog. 306 if (mBatteryProps.batteryTemperature > mShutdownBatteryTemperature) { 307 mHandler.post(new Runnable() { 308 @Override 309 public void run() { 310 if (mActivityManagerInternal.isSystemReady()) { 311 Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); 312 intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); 313 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 314 mContext.startActivityAsUser(intent, UserHandle.CURRENT); 315 } 316 } 317 }); 318 } 319 } 320 321 private void update(BatteryProperties props) { 322 synchronized (mLock) { 323 if (!mUpdatesStopped) { 324 mBatteryProps = props; 325 // Process the new values. 326 processValuesLocked(false); 327 } else { 328 mLastBatteryProps.set(props); 329 } 330 } 331 } 332 333 private void processValuesLocked(boolean force) { 334 boolean logOutlier = false; 335 long dischargeDuration = 0; 336 337 mBatteryLevelCritical = (mBatteryProps.batteryLevel <= mCriticalBatteryLevel); 338 if (mBatteryProps.chargerAcOnline) { 339 mPlugType = BatteryManager.BATTERY_PLUGGED_AC; 340 } else if (mBatteryProps.chargerUsbOnline) { 341 mPlugType = BatteryManager.BATTERY_PLUGGED_USB; 342 } else if (mBatteryProps.chargerWirelessOnline) { 343 mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS; 344 } else { 345 mPlugType = BATTERY_PLUGGED_NONE; 346 } 347 348 if (DEBUG) { 349 Slog.d(TAG, "Processing new values: " 350 + "chargerAcOnline=" + mBatteryProps.chargerAcOnline 351 + ", chargerUsbOnline=" + mBatteryProps.chargerUsbOnline 352 + ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline 353 + ", maxChargingCurrent" + mBatteryProps.maxChargingCurrent 354 + ", maxChargingVoltage" + mBatteryProps.maxChargingVoltage 355 + ", batteryStatus=" + mBatteryProps.batteryStatus 356 + ", batteryHealth=" + mBatteryProps.batteryHealth 357 + ", batteryPresent=" + mBatteryProps.batteryPresent 358 + ", batteryLevel=" + mBatteryProps.batteryLevel 359 + ", batteryTechnology=" + mBatteryProps.batteryTechnology 360 + ", batteryVoltage=" + mBatteryProps.batteryVoltage 361 + ", batteryChargeCounter=" + mBatteryProps.batteryChargeCounter 362 + ", batteryFullCharge=" + mBatteryProps.batteryFullCharge 363 + ", batteryTemperature=" + mBatteryProps.batteryTemperature 364 + ", mBatteryLevelCritical=" + mBatteryLevelCritical 365 + ", mPlugType=" + mPlugType); 366 } 367 368 // Let the battery stats keep track of the current level. 369 try { 370 mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth, 371 mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature, 372 mBatteryProps.batteryVoltage, mBatteryProps.batteryChargeCounter, 373 mBatteryProps.batteryFullCharge); 374 } catch (RemoteException e) { 375 // Should never happen. 376 } 377 378 shutdownIfNoPowerLocked(); 379 shutdownIfOverTempLocked(); 380 381 if (force || (mBatteryProps.batteryStatus != mLastBatteryStatus || 382 mBatteryProps.batteryHealth != mLastBatteryHealth || 383 mBatteryProps.batteryPresent != mLastBatteryPresent || 384 mBatteryProps.batteryLevel != mLastBatteryLevel || 385 mPlugType != mLastPlugType || 386 mBatteryProps.batteryVoltage != mLastBatteryVoltage || 387 mBatteryProps.batteryTemperature != mLastBatteryTemperature || 388 mBatteryProps.maxChargingCurrent != mLastMaxChargingCurrent || 389 mBatteryProps.maxChargingVoltage != mLastMaxChargingVoltage || 390 mBatteryProps.batteryChargeCounter != mLastChargeCounter || 391 mInvalidCharger != mLastInvalidCharger)) { 392 393 if (mPlugType != mLastPlugType) { 394 if (mLastPlugType == BATTERY_PLUGGED_NONE) { 395 // discharging -> charging 396 397 // There's no value in this data unless we've discharged at least once and the 398 // battery level has changed; so don't log until it does. 399 if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryProps.batteryLevel) { 400 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime; 401 logOutlier = true; 402 EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration, 403 mDischargeStartLevel, mBatteryProps.batteryLevel); 404 // make sure we see a discharge event before logging again 405 mDischargeStartTime = 0; 406 } 407 } else if (mPlugType == BATTERY_PLUGGED_NONE) { 408 // charging -> discharging or we just powered up 409 mDischargeStartTime = SystemClock.elapsedRealtime(); 410 mDischargeStartLevel = mBatteryProps.batteryLevel; 411 } 412 } 413 if (mBatteryProps.batteryStatus != mLastBatteryStatus || 414 mBatteryProps.batteryHealth != mLastBatteryHealth || 415 mBatteryProps.batteryPresent != mLastBatteryPresent || 416 mPlugType != mLastPlugType) { 417 EventLog.writeEvent(EventLogTags.BATTERY_STATUS, 418 mBatteryProps.batteryStatus, mBatteryProps.batteryHealth, mBatteryProps.batteryPresent ? 1 : 0, 419 mPlugType, mBatteryProps.batteryTechnology); 420 } 421 if (mBatteryProps.batteryLevel != mLastBatteryLevel) { 422 // Don't do this just from voltage or temperature changes, that is 423 // too noisy. 424 EventLog.writeEvent(EventLogTags.BATTERY_LEVEL, 425 mBatteryProps.batteryLevel, mBatteryProps.batteryVoltage, mBatteryProps.batteryTemperature); 426 } 427 if (mBatteryLevelCritical && !mLastBatteryLevelCritical && 428 mPlugType == BATTERY_PLUGGED_NONE) { 429 // We want to make sure we log discharge cycle outliers 430 // if the battery is about to die. 431 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime; 432 logOutlier = true; 433 } 434 435 if (!mBatteryLevelLow) { 436 // Should we now switch in to low battery mode? 437 if (mPlugType == BATTERY_PLUGGED_NONE 438 && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel) { 439 mBatteryLevelLow = true; 440 } 441 } else { 442 // Should we now switch out of low battery mode? 443 if (mPlugType != BATTERY_PLUGGED_NONE) { 444 mBatteryLevelLow = false; 445 } else if (mBatteryProps.batteryLevel >= mLowBatteryCloseWarningLevel) { 446 mBatteryLevelLow = false; 447 } else if (force && mBatteryProps.batteryLevel >= mLowBatteryWarningLevel) { 448 // If being forced, the previous state doesn't matter, we will just 449 // absolutely check to see if we are now above the warning level. 450 mBatteryLevelLow = false; 451 } 452 } 453 454 mSequence++; 455 456 // Separate broadcast is sent for power connected / not connected 457 // since the standard intent will not wake any applications and some 458 // applications may want to have smart behavior based on this. 459 if (mPlugType != 0 && mLastPlugType == 0) { 460 final Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED); 461 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 462 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); 463 mHandler.post(new Runnable() { 464 @Override 465 public void run() { 466 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); 467 } 468 }); 469 } 470 else if (mPlugType == 0 && mLastPlugType != 0) { 471 final Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED); 472 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 473 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); 474 mHandler.post(new Runnable() { 475 @Override 476 public void run() { 477 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); 478 } 479 }); 480 } 481 482 if (shouldSendBatteryLowLocked()) { 483 mSentLowBatteryBroadcast = true; 484 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW); 485 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 486 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); 487 mHandler.post(new Runnable() { 488 @Override 489 public void run() { 490 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); 491 } 492 }); 493 } else if (mSentLowBatteryBroadcast && 494 mBatteryProps.batteryLevel >= mLowBatteryCloseWarningLevel) { 495 mSentLowBatteryBroadcast = false; 496 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY); 497 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 498 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); 499 mHandler.post(new Runnable() { 500 @Override 501 public void run() { 502 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); 503 } 504 }); 505 } 506 507 // We are doing this after sending the above broadcasts, so anything processing 508 // them will get the new sequence number at that point. (See for example how testing 509 // of JobScheduler's BatteryController works.) 510 sendIntentLocked(); 511 512 // Update the battery LED 513 mLed.updateLightsLocked(); 514 515 // This needs to be done after sendIntent() so that we get the lastest battery stats. 516 if (logOutlier && dischargeDuration != 0) { 517 logOutlierLocked(dischargeDuration); 518 } 519 520 mLastBatteryStatus = mBatteryProps.batteryStatus; 521 mLastBatteryHealth = mBatteryProps.batteryHealth; 522 mLastBatteryPresent = mBatteryProps.batteryPresent; 523 mLastBatteryLevel = mBatteryProps.batteryLevel; 524 mLastPlugType = mPlugType; 525 mLastBatteryVoltage = mBatteryProps.batteryVoltage; 526 mLastBatteryTemperature = mBatteryProps.batteryTemperature; 527 mLastMaxChargingCurrent = mBatteryProps.maxChargingCurrent; 528 mLastMaxChargingVoltage = mBatteryProps.maxChargingVoltage; 529 mLastChargeCounter = mBatteryProps.batteryChargeCounter; 530 mLastBatteryLevelCritical = mBatteryLevelCritical; 531 mLastInvalidCharger = mInvalidCharger; 532 } 533 } 534 535 private void sendIntentLocked() { 536 // Pack up the values and broadcast them to everyone 537 final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); 538 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY 539 | Intent.FLAG_RECEIVER_REPLACE_PENDING); 540 541 int icon = getIconLocked(mBatteryProps.batteryLevel); 542 543 intent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); 544 intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryProps.batteryStatus); 545 intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryProps.batteryHealth); 546 intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryProps.batteryPresent); 547 intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryProps.batteryLevel); 548 intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE); 549 intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon); 550 intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType); 551 intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryProps.batteryVoltage); 552 intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryProps.batteryTemperature); 553 intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryProps.batteryTechnology); 554 intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger); 555 intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mBatteryProps.maxChargingCurrent); 556 intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mBatteryProps.maxChargingVoltage); 557 intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mBatteryProps.batteryChargeCounter); 558 if (DEBUG) { 559 Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. level:" + mBatteryProps.batteryLevel + 560 ", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus + 561 ", health:" + mBatteryProps.batteryHealth + 562 ", present:" + mBatteryProps.batteryPresent + 563 ", voltage: " + mBatteryProps.batteryVoltage + 564 ", temperature: " + mBatteryProps.batteryTemperature + 565 ", technology: " + mBatteryProps.batteryTechnology + 566 ", AC powered:" + mBatteryProps.chargerAcOnline + 567 ", USB powered:" + mBatteryProps.chargerUsbOnline + 568 ", Wireless powered:" + mBatteryProps.chargerWirelessOnline + 569 ", icon:" + icon + ", invalid charger:" + mInvalidCharger + 570 ", maxChargingCurrent:" + mBatteryProps.maxChargingCurrent + 571 ", maxChargingVoltage:" + mBatteryProps.maxChargingVoltage + 572 ", chargeCounter:" + mBatteryProps.batteryChargeCounter); 573 } 574 575 mHandler.post(new Runnable() { 576 @Override 577 public void run() { 578 ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL); 579 } 580 }); 581 } 582 583 private void logBatteryStatsLocked() { 584 IBinder batteryInfoService = ServiceManager.getService(BatteryStats.SERVICE_NAME); 585 if (batteryInfoService == null) return; 586 587 DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE); 588 if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return; 589 590 File dumpFile = null; 591 FileOutputStream dumpStream = null; 592 try { 593 // dump the service to a file 594 dumpFile = new File(DUMPSYS_DATA_PATH + BatteryStats.SERVICE_NAME + ".dump"); 595 dumpStream = new FileOutputStream(dumpFile); 596 batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS); 597 FileUtils.sync(dumpStream); 598 599 // add dump file to drop box 600 db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT); 601 } catch (RemoteException e) { 602 Slog.e(TAG, "failed to dump battery service", e); 603 } catch (IOException e) { 604 Slog.e(TAG, "failed to write dumpsys file", e); 605 } finally { 606 // make sure we clean up 607 if (dumpStream != null) { 608 try { 609 dumpStream.close(); 610 } catch (IOException e) { 611 Slog.e(TAG, "failed to close dumpsys output stream"); 612 } 613 } 614 if (dumpFile != null && !dumpFile.delete()) { 615 Slog.e(TAG, "failed to delete temporary dumpsys file: " 616 + dumpFile.getAbsolutePath()); 617 } 618 } 619 } 620 621 private void logOutlierLocked(long duration) { 622 ContentResolver cr = mContext.getContentResolver(); 623 String dischargeThresholdString = Settings.Global.getString(cr, 624 Settings.Global.BATTERY_DISCHARGE_THRESHOLD); 625 String durationThresholdString = Settings.Global.getString(cr, 626 Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD); 627 628 if (dischargeThresholdString != null && durationThresholdString != null) { 629 try { 630 long durationThreshold = Long.parseLong(durationThresholdString); 631 int dischargeThreshold = Integer.parseInt(dischargeThresholdString); 632 if (duration <= durationThreshold && 633 mDischargeStartLevel - mBatteryProps.batteryLevel >= dischargeThreshold) { 634 // If the discharge cycle is bad enough we want to know about it. 635 logBatteryStatsLocked(); 636 } 637 if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold + 638 " discharge threshold: " + dischargeThreshold); 639 if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " + 640 (mDischargeStartLevel - mBatteryProps.batteryLevel)); 641 } catch (NumberFormatException e) { 642 Slog.e(TAG, "Invalid DischargeThresholds GService string: " + 643 durationThresholdString + " or " + dischargeThresholdString); 644 } 645 } 646 } 647 648 private int getIconLocked(int level) { 649 if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) { 650 return com.android.internal.R.drawable.stat_sys_battery_charge; 651 } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) { 652 return com.android.internal.R.drawable.stat_sys_battery; 653 } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING 654 || mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) { 655 if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY) 656 && mBatteryProps.batteryLevel >= 100) { 657 return com.android.internal.R.drawable.stat_sys_battery_charge; 658 } else { 659 return com.android.internal.R.drawable.stat_sys_battery; 660 } 661 } else { 662 return com.android.internal.R.drawable.stat_sys_battery_unknown; 663 } 664 } 665 666 class Shell extends ShellCommand { 667 @Override 668 public int onCommand(String cmd) { 669 return onShellCommand(this, cmd); 670 } 671 672 @Override 673 public void onHelp() { 674 PrintWriter pw = getOutPrintWriter(); 675 dumpHelp(pw); 676 } 677 } 678 679 static void dumpHelp(PrintWriter pw) { 680 pw.println("Battery service (battery) commands:"); 681 pw.println(" help"); 682 pw.println(" Print this help text."); 683 pw.println(" set [-f] [ac|usb|wireless|status|level|temp|present|invalid] <value>"); 684 pw.println(" Force a battery property value, freezing battery state."); 685 pw.println(" -f: force a battery change broadcast be sent, prints new sequence."); 686 pw.println(" unplug [-f]"); 687 pw.println(" Force battery unplugged, freezing battery state."); 688 pw.println(" -f: force a battery change broadcast be sent, prints new sequence."); 689 pw.println(" reset [-f]"); 690 pw.println(" Unfreeze battery state, returning to current hardware values."); 691 pw.println(" -f: force a battery change broadcast be sent, prints new sequence."); 692 } 693 694 static final int OPTION_FORCE_UPDATE = 1<<0; 695 696 int parseOptions(Shell shell) { 697 String opt; 698 int opts = 0; 699 while ((opt = shell.getNextOption()) != null) { 700 if ("-f".equals(opt)) { 701 opts |= OPTION_FORCE_UPDATE; 702 } 703 } 704 return opts; 705 } 706 707 int onShellCommand(Shell shell, String cmd) { 708 if (cmd == null) { 709 return shell.handleDefaultCommands(cmd); 710 } 711 PrintWriter pw = shell.getOutPrintWriter(); 712 switch (cmd) { 713 case "unplug": { 714 int opts = parseOptions(shell); 715 getContext().enforceCallingOrSelfPermission( 716 android.Manifest.permission.DEVICE_POWER, null); 717 if (!mUpdatesStopped) { 718 mLastBatteryProps.set(mBatteryProps); 719 } 720 mBatteryProps.chargerAcOnline = false; 721 mBatteryProps.chargerUsbOnline = false; 722 mBatteryProps.chargerWirelessOnline = false; 723 long ident = Binder.clearCallingIdentity(); 724 try { 725 mUpdatesStopped = true; 726 processValuesFromShellLocked(pw, opts); 727 } finally { 728 Binder.restoreCallingIdentity(ident); 729 } 730 } break; 731 case "set": { 732 int opts = parseOptions(shell); 733 getContext().enforceCallingOrSelfPermission( 734 android.Manifest.permission.DEVICE_POWER, null); 735 final String key = shell.getNextArg(); 736 if (key == null) { 737 pw.println("No property specified"); 738 return -1; 739 740 } 741 final String value = shell.getNextArg(); 742 if (value == null) { 743 pw.println("No value specified"); 744 return -1; 745 746 } 747 try { 748 if (!mUpdatesStopped) { 749 mLastBatteryProps.set(mBatteryProps); 750 } 751 boolean update = true; 752 switch (key) { 753 case "present": 754 mBatteryProps.batteryPresent = Integer.parseInt(value) != 0; 755 break; 756 case "ac": 757 mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0; 758 break; 759 case "usb": 760 mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0; 761 break; 762 case "wireless": 763 mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0; 764 break; 765 case "status": 766 mBatteryProps.batteryStatus = Integer.parseInt(value); 767 break; 768 case "level": 769 mBatteryProps.batteryLevel = Integer.parseInt(value); 770 break; 771 case "temp": 772 mBatteryProps.batteryTemperature = Integer.parseInt(value); 773 break; 774 case "invalid": 775 mInvalidCharger = Integer.parseInt(value); 776 break; 777 default: 778 pw.println("Unknown set option: " + key); 779 update = false; 780 break; 781 } 782 if (update) { 783 long ident = Binder.clearCallingIdentity(); 784 try { 785 mUpdatesStopped = true; 786 processValuesFromShellLocked(pw, opts); 787 } finally { 788 Binder.restoreCallingIdentity(ident); 789 } 790 } 791 } catch (NumberFormatException ex) { 792 pw.println("Bad value: " + value); 793 return -1; 794 } 795 } break; 796 case "reset": { 797 int opts = parseOptions(shell); 798 getContext().enforceCallingOrSelfPermission( 799 android.Manifest.permission.DEVICE_POWER, null); 800 long ident = Binder.clearCallingIdentity(); 801 try { 802 if (mUpdatesStopped) { 803 mUpdatesStopped = false; 804 mBatteryProps.set(mLastBatteryProps); 805 processValuesFromShellLocked(pw, opts); 806 } 807 } finally { 808 Binder.restoreCallingIdentity(ident); 809 } 810 } break; 811 default: 812 return shell.handleDefaultCommands(cmd); 813 } 814 return 0; 815 } 816 817 private void processValuesFromShellLocked(PrintWriter pw, int opts) { 818 processValuesLocked((opts & OPTION_FORCE_UPDATE) != 0); 819 if ((opts & OPTION_FORCE_UPDATE) != 0) { 820 pw.println(mSequence); 821 } 822 } 823 824 private void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) { 825 synchronized (mLock) { 826 if (args == null || args.length == 0 || "-a".equals(args[0])) { 827 pw.println("Current Battery Service state:"); 828 if (mUpdatesStopped) { 829 pw.println(" (UPDATES STOPPED -- use 'reset' to restart)"); 830 } 831 pw.println(" AC powered: " + mBatteryProps.chargerAcOnline); 832 pw.println(" USB powered: " + mBatteryProps.chargerUsbOnline); 833 pw.println(" Wireless powered: " + mBatteryProps.chargerWirelessOnline); 834 pw.println(" Max charging current: " + mBatteryProps.maxChargingCurrent); 835 pw.println(" Max charging voltage: " + mBatteryProps.maxChargingVoltage); 836 pw.println(" Charge counter: " + mBatteryProps.batteryChargeCounter); 837 pw.println(" status: " + mBatteryProps.batteryStatus); 838 pw.println(" health: " + mBatteryProps.batteryHealth); 839 pw.println(" present: " + mBatteryProps.batteryPresent); 840 pw.println(" level: " + mBatteryProps.batteryLevel); 841 pw.println(" scale: " + BATTERY_SCALE); 842 pw.println(" voltage: " + mBatteryProps.batteryVoltage); 843 pw.println(" temperature: " + mBatteryProps.batteryTemperature); 844 pw.println(" technology: " + mBatteryProps.batteryTechnology); 845 } else { 846 Shell shell = new Shell(); 847 shell.exec(mBinderService, null, fd, null, args, null, new ResultReceiver(null)); 848 } 849 } 850 } 851 852 private void dumpProto(FileDescriptor fd) { 853 final ProtoOutputStream proto = new ProtoOutputStream(fd); 854 855 synchronized (mLock) { 856 proto.write(BatteryServiceDumpProto.ARE_UPDATES_STOPPED, mUpdatesStopped); 857 int batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_NONE; 858 if (mBatteryProps.chargerAcOnline) { 859 batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_AC; 860 } else if (mBatteryProps.chargerUsbOnline) { 861 batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_USB; 862 } else if (mBatteryProps.chargerWirelessOnline) { 863 batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_WIRELESS; 864 } 865 proto.write(BatteryServiceDumpProto.PLUGGED, batteryPluggedValue); 866 proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mBatteryProps.maxChargingCurrent); 867 proto.write(BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE, mBatteryProps.maxChargingVoltage); 868 proto.write(BatteryServiceDumpProto.CHARGE_COUNTER, mBatteryProps.batteryChargeCounter); 869 proto.write(BatteryServiceDumpProto.STATUS, mBatteryProps.batteryStatus); 870 proto.write(BatteryServiceDumpProto.HEALTH, mBatteryProps.batteryHealth); 871 proto.write(BatteryServiceDumpProto.IS_PRESENT, mBatteryProps.batteryPresent); 872 proto.write(BatteryServiceDumpProto.LEVEL, mBatteryProps.batteryLevel); 873 proto.write(BatteryServiceDumpProto.SCALE, BATTERY_SCALE); 874 proto.write(BatteryServiceDumpProto.VOLTAGE, mBatteryProps.batteryVoltage); 875 proto.write(BatteryServiceDumpProto.TEMPERATURE, mBatteryProps.batteryTemperature); 876 proto.write(BatteryServiceDumpProto.TECHNOLOGY, mBatteryProps.batteryTechnology); 877 } 878 proto.flush(); 879 } 880 881 private final class Led { 882 private final Light mBatteryLight; 883 884 private final int mBatteryLowARGB; 885 private final int mBatteryMediumARGB; 886 private final int mBatteryFullARGB; 887 private final int mBatteryLedOn; 888 private final int mBatteryLedOff; 889 890 public Led(Context context, LightsManager lights) { 891 mBatteryLight = lights.getLight(LightsManager.LIGHT_ID_BATTERY); 892 893 mBatteryLowARGB = context.getResources().getInteger( 894 com.android.internal.R.integer.config_notificationsBatteryLowARGB); 895 mBatteryMediumARGB = context.getResources().getInteger( 896 com.android.internal.R.integer.config_notificationsBatteryMediumARGB); 897 mBatteryFullARGB = context.getResources().getInteger( 898 com.android.internal.R.integer.config_notificationsBatteryFullARGB); 899 mBatteryLedOn = context.getResources().getInteger( 900 com.android.internal.R.integer.config_notificationsBatteryLedOn); 901 mBatteryLedOff = context.getResources().getInteger( 902 com.android.internal.R.integer.config_notificationsBatteryLedOff); 903 } 904 905 /** 906 * Synchronize on BatteryService. 907 */ 908 public void updateLightsLocked() { 909 final int level = mBatteryProps.batteryLevel; 910 final int status = mBatteryProps.batteryStatus; 911 if (level < mLowBatteryWarningLevel) { 912 if (status == BatteryManager.BATTERY_STATUS_CHARGING) { 913 // Solid red when battery is charging 914 mBatteryLight.setColor(mBatteryLowARGB); 915 } else { 916 // Flash red when battery is low and not charging 917 mBatteryLight.setFlashing(mBatteryLowARGB, Light.LIGHT_FLASH_TIMED, 918 mBatteryLedOn, mBatteryLedOff); 919 } 920 } else if (status == BatteryManager.BATTERY_STATUS_CHARGING 921 || status == BatteryManager.BATTERY_STATUS_FULL) { 922 if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) { 923 // Solid green when full or charging and nearly full 924 mBatteryLight.setColor(mBatteryFullARGB); 925 } else { 926 // Solid orange when charging and halfway full 927 mBatteryLight.setColor(mBatteryMediumARGB); 928 } 929 } else { 930 // No lights if not charging and not low 931 mBatteryLight.turnOff(); 932 } 933 } 934 } 935 936 private final class BatteryListener extends IBatteryPropertiesListener.Stub { 937 @Override public void batteryPropertiesChanged(BatteryProperties props) { 938 final long identity = Binder.clearCallingIdentity(); 939 try { 940 BatteryService.this.update(props); 941 } finally { 942 Binder.restoreCallingIdentity(identity); 943 } 944 } 945 } 946 947 private final class BinderService extends Binder { 948 @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 949 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 950 951 if (args.length > 0 && "--proto".equals(args[0])) { 952 dumpProto(fd); 953 } else { 954 dumpInternal(fd, pw, args); 955 } 956 } 957 958 @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, 959 FileDescriptor err, String[] args, ShellCallback callback, 960 ResultReceiver resultReceiver) { 961 (new Shell()).exec(this, in, out, err, args, callback, resultReceiver); 962 } 963 } 964 965 private final class LocalService extends BatteryManagerInternal { 966 @Override 967 public boolean isPowered(int plugTypeSet) { 968 synchronized (mLock) { 969 return isPoweredLocked(plugTypeSet); 970 } 971 } 972 973 @Override 974 public int getPlugType() { 975 synchronized (mLock) { 976 return mPlugType; 977 } 978 } 979 980 @Override 981 public int getBatteryLevel() { 982 synchronized (mLock) { 983 return mBatteryProps.batteryLevel; 984 } 985 } 986 987 @Override 988 public boolean getBatteryLevelLow() { 989 synchronized (mLock) { 990 return mBatteryLevelLow; 991 } 992 } 993 994 @Override 995 public int getInvalidCharger() { 996 synchronized (mLock) { 997 return mInvalidCharger; 998 } 999 } 1000 } 1001 } 1002