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