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