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