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