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