BatteryService.java revision 37a37f445cbcd64376af153dd79ea2a123d9405d
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 com.android.internal.app.IBatteryStats; 20import com.android.server.am.BatteryStatsService; 21 22import android.app.ActivityManagerNative; 23import android.content.ContentResolver; 24import android.content.Context; 25import android.content.Intent; 26import android.content.pm.PackageManager; 27import android.os.BatteryManager; 28import android.os.Binder; 29import android.os.FileUtils; 30import android.os.IBinder; 31import android.os.DropBoxManager; 32import android.os.RemoteException; 33import android.os.ServiceManager; 34import android.os.SystemClock; 35import android.os.UEventObserver; 36import android.provider.Settings; 37import android.util.EventLog; 38import android.util.Slog; 39 40import java.io.File; 41import java.io.FileDescriptor; 42import java.io.FileInputStream; 43import java.io.FileOutputStream; 44import java.io.IOException; 45import java.io.PrintWriter; 46import java.util.Arrays; 47 48 49/** 50 * <p>BatteryService monitors the charging status, and charge level of the device 51 * battery. When these values change this service broadcasts the new values 52 * to all {@link android.content.BroadcastReceiver IntentReceivers} that are 53 * watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED 54 * BATTERY_CHANGED} action.</p> 55 * <p>The new values are stored in the Intent data and can be retrieved by 56 * calling {@link android.content.Intent#getExtra Intent.getExtra} with the 57 * following keys:</p> 58 * <p>"scale" - int, the maximum value for the charge level</p> 59 * <p>"level" - int, charge level, from 0 through "scale" inclusive</p> 60 * <p>"status" - String, the current charging status.<br /> 61 * <p>"health" - String, the current battery health.<br /> 62 * <p>"present" - boolean, true if the battery is present<br /> 63 * <p>"icon-small" - int, suggested small icon to use for this state</p> 64 * <p>"plugged" - int, 0 if the device is not plugged in; 1 if plugged 65 * into an AC power adapter; 2 if plugged in via USB.</p> 66 * <p>"voltage" - int, current battery voltage in millivolts</p> 67 * <p>"temperature" - int, current battery temperature in tenths of 68 * a degree Centigrade</p> 69 * <p>"technology" - String, the type of battery installed, e.g. "Li-ion"</p> 70 */ 71public class BatteryService extends Binder { 72 private static final String TAG = BatteryService.class.getSimpleName(); 73 74 private static final boolean LOCAL_LOGV = false; 75 76 static final int BATTERY_SCALE = 100; // battery capacity is a percentage 77 78 // Used locally for determining when to make a last ditch effort to log 79 // discharge stats before the device dies. 80 private int mCriticalBatteryLevel; 81 82 private static final int DUMP_MAX_LENGTH = 24 * 1024; 83 private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "-u" }; 84 private static final String BATTERY_STATS_SERVICE_NAME = "batteryinfo"; 85 86 private static final String DUMPSYS_DATA_PATH = "/data/system/"; 87 88 // This should probably be exposed in the API, though it's not critical 89 private static final int BATTERY_PLUGGED_NONE = 0; 90 91 private final Context mContext; 92 private final IBatteryStats mBatteryStats; 93 94 private boolean mAcOnline; 95 private boolean mUsbOnline; 96 private boolean mWirelessOnline; 97 private int mBatteryStatus; 98 private int mBatteryHealth; 99 private boolean mBatteryPresent; 100 private int mBatteryLevel; 101 private int mBatteryVoltage; 102 private int mBatteryTemperature; 103 private String mBatteryTechnology; 104 private boolean mBatteryLevelCritical; 105 private int mInvalidCharger; 106 107 private int mLastBatteryStatus; 108 private int mLastBatteryHealth; 109 private boolean mLastBatteryPresent; 110 private int mLastBatteryLevel; 111 private int mLastBatteryVoltage; 112 private int mLastBatteryTemperature; 113 private boolean mLastBatteryLevelCritical; 114 private int mLastInvalidCharger; 115 116 private int mLowBatteryWarningLevel; 117 private int mLowBatteryCloseWarningLevel; 118 119 private int mPlugType; 120 private int mLastPlugType = -1; // Extra state so we can detect first run 121 122 private long mDischargeStartTime; 123 private int mDischargeStartLevel; 124 125 private Led mLed; 126 127 private boolean mSentLowBatteryBroadcast = false; 128 129 public BatteryService(Context context, LightsService lights) { 130 mContext = context; 131 mLed = new Led(context, lights); 132 mBatteryStats = BatteryStatsService.getService(); 133 134 mCriticalBatteryLevel = mContext.getResources().getInteger( 135 com.android.internal.R.integer.config_criticalBatteryWarningLevel); 136 mLowBatteryWarningLevel = mContext.getResources().getInteger( 137 com.android.internal.R.integer.config_lowBatteryWarningLevel); 138 mLowBatteryCloseWarningLevel = mContext.getResources().getInteger( 139 com.android.internal.R.integer.config_lowBatteryCloseWarningLevel); 140 141 mPowerSupplyObserver.startObserving("SUBSYSTEM=power_supply"); 142 143 // watch for invalid charger messages if the invalid_charger switch exists 144 if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) { 145 mInvalidChargerObserver.startObserving("DEVPATH=/devices/virtual/switch/invalid_charger"); 146 } 147 148 // set initial status 149 update(); 150 } 151 152 public final boolean isPowered() { 153 // assume we are powered if battery state is unknown so the "stay on while plugged in" option will work. 154 return (mAcOnline || mUsbOnline || mWirelessOnline 155 || mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN); 156 } 157 158 public final boolean isPowered(int plugTypeSet) { 159 // assume we are powered if battery state is unknown so 160 // the "stay on while plugged in" option will work. 161 if (mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) { 162 return true; 163 } 164 if (plugTypeSet == 0) { 165 return false; 166 } 167 int plugTypeBit = 0; 168 if (mAcOnline) { 169 plugTypeBit |= BatteryManager.BATTERY_PLUGGED_AC; 170 } 171 if (mUsbOnline) { 172 plugTypeBit |= BatteryManager.BATTERY_PLUGGED_USB; 173 } 174 if (mWirelessOnline) { 175 plugTypeBit |= BatteryManager.BATTERY_PLUGGED_WIRELESS; 176 } 177 return (plugTypeSet & plugTypeBit) != 0; 178 } 179 180 public final int getPlugType() { 181 return mPlugType; 182 } 183 184 private UEventObserver mPowerSupplyObserver = new UEventObserver() { 185 @Override 186 public void onUEvent(UEventObserver.UEvent event) { 187 update(); 188 } 189 }; 190 191 private UEventObserver mInvalidChargerObserver = new UEventObserver() { 192 @Override 193 public void onUEvent(UEventObserver.UEvent event) { 194 int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0; 195 if (mInvalidCharger != invalidCharger) { 196 mInvalidCharger = invalidCharger; 197 update(); 198 } 199 } 200 }; 201 202 // returns battery level as a percentage 203 public final int getBatteryLevel() { 204 return mBatteryLevel; 205 } 206 207 void systemReady() { 208 // check our power situation now that it is safe to display the shutdown dialog. 209 shutdownIfNoPower(); 210 shutdownIfOverTemp(); 211 } 212 213 private final void shutdownIfNoPower() { 214 // shut down gracefully if our battery is critically low and we are not powered. 215 // wait until the system has booted before attempting to display the shutdown dialog. 216 if (mBatteryLevel == 0 && !isPowered() && ActivityManagerNative.isSystemReady()) { 217 Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); 218 intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); 219 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 220 mContext.startActivity(intent); 221 } 222 } 223 224 private final void shutdownIfOverTemp() { 225 // shut down gracefully if temperature is too high (> 68.0C) 226 // wait until the system has booted before attempting to display the shutdown dialog. 227 if (mBatteryTemperature > 680 && ActivityManagerNative.isSystemReady()) { 228 Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); 229 intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); 230 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 231 mContext.startActivity(intent); 232 } 233 } 234 235 private native void native_update(); 236 237 private synchronized final void update() { 238 native_update(); 239 processValues(); 240 } 241 242 private void processValues() { 243 boolean logOutlier = false; 244 long dischargeDuration = 0; 245 246 mBatteryLevelCritical = mBatteryLevel <= mCriticalBatteryLevel; 247 if (mAcOnline) { 248 mPlugType = BatteryManager.BATTERY_PLUGGED_AC; 249 } else if (mUsbOnline) { 250 mPlugType = BatteryManager.BATTERY_PLUGGED_USB; 251 } else if (mWirelessOnline) { 252 mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS; 253 } else { 254 mPlugType = BATTERY_PLUGGED_NONE; 255 } 256 257 // Let the battery stats keep track of the current level. 258 try { 259 mBatteryStats.setBatteryState(mBatteryStatus, mBatteryHealth, 260 mPlugType, mBatteryLevel, mBatteryTemperature, 261 mBatteryVoltage); 262 } catch (RemoteException e) { 263 // Should never happen. 264 } 265 266 shutdownIfNoPower(); 267 shutdownIfOverTemp(); 268 269 if (mBatteryStatus != mLastBatteryStatus || 270 mBatteryHealth != mLastBatteryHealth || 271 mBatteryPresent != mLastBatteryPresent || 272 mBatteryLevel != mLastBatteryLevel || 273 mPlugType != mLastPlugType || 274 mBatteryVoltage != mLastBatteryVoltage || 275 mBatteryTemperature != mLastBatteryTemperature || 276 mInvalidCharger != mLastInvalidCharger) { 277 278 if (mPlugType != mLastPlugType) { 279 if (mLastPlugType == BATTERY_PLUGGED_NONE) { 280 // discharging -> charging 281 282 // There's no value in this data unless we've discharged at least once and the 283 // battery level has changed; so don't log until it does. 284 if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryLevel) { 285 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime; 286 logOutlier = true; 287 EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration, 288 mDischargeStartLevel, mBatteryLevel); 289 // make sure we see a discharge event before logging again 290 mDischargeStartTime = 0; 291 } 292 } else if (mPlugType == BATTERY_PLUGGED_NONE) { 293 // charging -> discharging or we just powered up 294 mDischargeStartTime = SystemClock.elapsedRealtime(); 295 mDischargeStartLevel = mBatteryLevel; 296 } 297 } 298 if (mBatteryStatus != mLastBatteryStatus || 299 mBatteryHealth != mLastBatteryHealth || 300 mBatteryPresent != mLastBatteryPresent || 301 mPlugType != mLastPlugType) { 302 EventLog.writeEvent(EventLogTags.BATTERY_STATUS, 303 mBatteryStatus, mBatteryHealth, mBatteryPresent ? 1 : 0, 304 mPlugType, mBatteryTechnology); 305 } 306 if (mBatteryLevel != mLastBatteryLevel || 307 mBatteryVoltage != mLastBatteryVoltage || 308 mBatteryTemperature != mLastBatteryTemperature) { 309 EventLog.writeEvent(EventLogTags.BATTERY_LEVEL, 310 mBatteryLevel, mBatteryVoltage, mBatteryTemperature); 311 } 312 if (mBatteryLevelCritical && !mLastBatteryLevelCritical && 313 mPlugType == BATTERY_PLUGGED_NONE) { 314 // We want to make sure we log discharge cycle outliers 315 // if the battery is about to die. 316 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime; 317 logOutlier = true; 318 } 319 320 final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE; 321 final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE; 322 323 /* The ACTION_BATTERY_LOW broadcast is sent in these situations: 324 * - is just un-plugged (previously was plugged) and battery level is 325 * less than or equal to WARNING, or 326 * - is not plugged and battery level falls to WARNING boundary 327 * (becomes <= mLowBatteryWarningLevel). 328 */ 329 final boolean sendBatteryLow = !plugged 330 && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN 331 && mBatteryLevel <= mLowBatteryWarningLevel 332 && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel); 333 334 sendIntent(); 335 336 // Separate broadcast is sent for power connected / not connected 337 // since the standard intent will not wake any applications and some 338 // applications may want to have smart behavior based on this. 339 Intent statusIntent = new Intent(); 340 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 341 if (mPlugType != 0 && mLastPlugType == 0) { 342 statusIntent.setAction(Intent.ACTION_POWER_CONNECTED); 343 mContext.sendBroadcast(statusIntent); 344 } 345 else if (mPlugType == 0 && mLastPlugType != 0) { 346 statusIntent.setAction(Intent.ACTION_POWER_DISCONNECTED); 347 mContext.sendBroadcast(statusIntent); 348 } 349 350 if (sendBatteryLow) { 351 mSentLowBatteryBroadcast = true; 352 statusIntent.setAction(Intent.ACTION_BATTERY_LOW); 353 mContext.sendBroadcast(statusIntent); 354 } else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) { 355 mSentLowBatteryBroadcast = false; 356 statusIntent.setAction(Intent.ACTION_BATTERY_OKAY); 357 mContext.sendBroadcast(statusIntent); 358 } 359 360 // Update the battery LED 361 mLed.updateLightsLocked(); 362 363 // This needs to be done after sendIntent() so that we get the lastest battery stats. 364 if (logOutlier && dischargeDuration != 0) { 365 logOutlier(dischargeDuration); 366 } 367 368 mLastBatteryStatus = mBatteryStatus; 369 mLastBatteryHealth = mBatteryHealth; 370 mLastBatteryPresent = mBatteryPresent; 371 mLastBatteryLevel = mBatteryLevel; 372 mLastPlugType = mPlugType; 373 mLastBatteryVoltage = mBatteryVoltage; 374 mLastBatteryTemperature = mBatteryTemperature; 375 mLastBatteryLevelCritical = mBatteryLevelCritical; 376 mLastInvalidCharger = mInvalidCharger; 377 } 378 } 379 380 private final void sendIntent() { 381 // Pack up the values and broadcast them to everyone 382 Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); 383 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY 384 | Intent.FLAG_RECEIVER_REPLACE_PENDING); 385 386 int icon = getIcon(mBatteryLevel); 387 388 intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryStatus); 389 intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryHealth); 390 intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryPresent); 391 intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryLevel); 392 intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE); 393 intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon); 394 intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType); 395 intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryVoltage); 396 intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryTemperature); 397 intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryTechnology); 398 intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger); 399 400 if (false) { 401 Slog.d(TAG, "level:" + mBatteryLevel + 402 " scale:" + BATTERY_SCALE + " status:" + mBatteryStatus + 403 " health:" + mBatteryHealth + " present:" + mBatteryPresent + 404 " voltage: " + mBatteryVoltage + 405 " temperature: " + mBatteryTemperature + 406 " technology: " + mBatteryTechnology + 407 " AC powered:" + mAcOnline + " USB powered:" + mUsbOnline + 408 " Wireless powered:" + mWirelessOnline + 409 " icon:" + icon + " invalid charger:" + mInvalidCharger); 410 } 411 412 ActivityManagerNative.broadcastStickyIntent(intent, null); 413 } 414 415 private final void logBatteryStats() { 416 IBinder batteryInfoService = ServiceManager.getService(BATTERY_STATS_SERVICE_NAME); 417 if (batteryInfoService == null) return; 418 419 DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE); 420 if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return; 421 422 File dumpFile = null; 423 FileOutputStream dumpStream = null; 424 try { 425 // dump the service to a file 426 dumpFile = new File(DUMPSYS_DATA_PATH + BATTERY_STATS_SERVICE_NAME + ".dump"); 427 dumpStream = new FileOutputStream(dumpFile); 428 batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS); 429 FileUtils.sync(dumpStream); 430 431 // add dump file to drop box 432 db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT); 433 } catch (RemoteException e) { 434 Slog.e(TAG, "failed to dump battery service", e); 435 } catch (IOException e) { 436 Slog.e(TAG, "failed to write dumpsys file", e); 437 } finally { 438 // make sure we clean up 439 if (dumpStream != null) { 440 try { 441 dumpStream.close(); 442 } catch (IOException e) { 443 Slog.e(TAG, "failed to close dumpsys output stream"); 444 } 445 } 446 if (dumpFile != null && !dumpFile.delete()) { 447 Slog.e(TAG, "failed to delete temporary dumpsys file: " 448 + dumpFile.getAbsolutePath()); 449 } 450 } 451 } 452 453 private final void logOutlier(long duration) { 454 ContentResolver cr = mContext.getContentResolver(); 455 String dischargeThresholdString = Settings.Secure.getString(cr, 456 Settings.Secure.BATTERY_DISCHARGE_THRESHOLD); 457 String durationThresholdString = Settings.Secure.getString(cr, 458 Settings.Secure.BATTERY_DISCHARGE_DURATION_THRESHOLD); 459 460 if (dischargeThresholdString != null && durationThresholdString != null) { 461 try { 462 long durationThreshold = Long.parseLong(durationThresholdString); 463 int dischargeThreshold = Integer.parseInt(dischargeThresholdString); 464 if (duration <= durationThreshold && 465 mDischargeStartLevel - mBatteryLevel >= dischargeThreshold) { 466 // If the discharge cycle is bad enough we want to know about it. 467 logBatteryStats(); 468 } 469 if (LOCAL_LOGV) Slog.v(TAG, "duration threshold: " + durationThreshold + 470 " discharge threshold: " + dischargeThreshold); 471 if (LOCAL_LOGV) Slog.v(TAG, "duration: " + duration + " discharge: " + 472 (mDischargeStartLevel - mBatteryLevel)); 473 } catch (NumberFormatException e) { 474 Slog.e(TAG, "Invalid DischargeThresholds GService string: " + 475 durationThresholdString + " or " + dischargeThresholdString); 476 return; 477 } 478 } 479 } 480 481 private final int getIcon(int level) { 482 if (mBatteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) { 483 return com.android.internal.R.drawable.stat_sys_battery_charge; 484 } else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) { 485 return com.android.internal.R.drawable.stat_sys_battery; 486 } else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING 487 || mBatteryStatus == BatteryManager.BATTERY_STATUS_FULL) { 488 if (isPowered() && mBatteryLevel >= 100) { 489 return com.android.internal.R.drawable.stat_sys_battery_charge; 490 } else { 491 return com.android.internal.R.drawable.stat_sys_battery; 492 } 493 } else { 494 return com.android.internal.R.drawable.stat_sys_battery_unknown; 495 } 496 } 497 498 @Override 499 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 500 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 501 != PackageManager.PERMISSION_GRANTED) { 502 503 pw.println("Permission Denial: can't dump Battery service from from pid=" 504 + Binder.getCallingPid() 505 + ", uid=" + Binder.getCallingUid()); 506 return; 507 } 508 509 if (args == null || args.length == 0 || "-a".equals(args[0])) { 510 synchronized (this) { 511 pw.println("Current Battery Service state:"); 512 pw.println(" AC powered: " + mAcOnline); 513 pw.println(" USB powered: " + mUsbOnline); 514 pw.println(" Wireless powered: " + mWirelessOnline); 515 pw.println(" status: " + mBatteryStatus); 516 pw.println(" health: " + mBatteryHealth); 517 pw.println(" present: " + mBatteryPresent); 518 pw.println(" level: " + mBatteryLevel); 519 pw.println(" scale: " + BATTERY_SCALE); 520 pw.println(" voltage:" + mBatteryVoltage); 521 pw.println(" temperature: " + mBatteryTemperature); 522 pw.println(" technology: " + mBatteryTechnology); 523 } 524 } else if (false) { 525 // DO NOT SUBMIT WITH THIS TURNED ON 526 if (args.length == 3 && "set".equals(args[0])) { 527 String key = args[1]; 528 String value = args[2]; 529 try { 530 boolean update = true; 531 if ("ac".equals(key)) { 532 mAcOnline = Integer.parseInt(value) != 0; 533 } else if ("usb".equals(key)) { 534 mUsbOnline = Integer.parseInt(value) != 0; 535 } else if ("wireless".equals(key)) { 536 mWirelessOnline = Integer.parseInt(value) != 0; 537 } else if ("status".equals(key)) { 538 mBatteryStatus = Integer.parseInt(value); 539 } else if ("level".equals(key)) { 540 mBatteryLevel = Integer.parseInt(value); 541 } else if ("invalid".equals(key)) { 542 mInvalidCharger = Integer.parseInt(value); 543 } else { 544 update = false; 545 } 546 if (update) { 547 processValues(); 548 } 549 } catch (NumberFormatException ex) { 550 pw.println("Bad value: " + value); 551 } 552 } 553 } 554 } 555 556 class Led { 557 private LightsService mLightsService; 558 private LightsService.Light mBatteryLight; 559 560 private int mBatteryLowARGB; 561 private int mBatteryMediumARGB; 562 private int mBatteryFullARGB; 563 private int mBatteryLedOn; 564 private int mBatteryLedOff; 565 566 private boolean mBatteryCharging; 567 private boolean mBatteryLow; 568 private boolean mBatteryFull; 569 570 Led(Context context, LightsService lights) { 571 mLightsService = lights; 572 mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY); 573 574 mBatteryLowARGB = mContext.getResources().getInteger( 575 com.android.internal.R.integer.config_notificationsBatteryLowARGB); 576 mBatteryMediumARGB = mContext.getResources().getInteger( 577 com.android.internal.R.integer.config_notificationsBatteryMediumARGB); 578 mBatteryFullARGB = mContext.getResources().getInteger( 579 com.android.internal.R.integer.config_notificationsBatteryFullARGB); 580 mBatteryLedOn = mContext.getResources().getInteger( 581 com.android.internal.R.integer.config_notificationsBatteryLedOn); 582 mBatteryLedOff = mContext.getResources().getInteger( 583 com.android.internal.R.integer.config_notificationsBatteryLedOff); 584 } 585 586 /** 587 * Synchronize on BatteryService. 588 */ 589 void updateLightsLocked() { 590 final int level = mBatteryLevel; 591 final int status = mBatteryStatus; 592 if (level < mLowBatteryWarningLevel) { 593 if (status == BatteryManager.BATTERY_STATUS_CHARGING) { 594 // Solid red when battery is charging 595 mBatteryLight.setColor(mBatteryLowARGB); 596 } else { 597 // Flash red when battery is low and not charging 598 mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED, 599 mBatteryLedOn, mBatteryLedOff); 600 } 601 } else if (status == BatteryManager.BATTERY_STATUS_CHARGING 602 || status == BatteryManager.BATTERY_STATUS_FULL) { 603 if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) { 604 // Solid green when full or charging and nearly full 605 mBatteryLight.setColor(mBatteryFullARGB); 606 } else { 607 // Solid orange when charging and halfway full 608 mBatteryLight.setColor(mBatteryMediumARGB); 609 } 610 } else { 611 // No lights if not charging and not low 612 mBatteryLight.turnOff(); 613 } 614 } 615 } 616} 617