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