BatteryStatsHelper.java revision 5b1308f02d720808727af61863bd59c227d6fe02
1/* 2 * Copyright (C) 2009 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.internal.os; 18 19import android.content.Context; 20import android.content.Intent; 21import android.content.IntentFilter; 22import android.hardware.SensorManager; 23import android.net.ConnectivityManager; 24import android.os.BatteryStats; 25import android.os.BatteryStats.Uid; 26import android.os.Bundle; 27import android.os.MemoryFile; 28import android.os.Parcel; 29import android.os.ParcelFileDescriptor; 30import android.os.Process; 31import android.os.RemoteException; 32import android.os.ServiceManager; 33import android.os.SystemClock; 34import android.os.UserHandle; 35import android.util.ArrayMap; 36import android.util.Log; 37import android.util.SparseArray; 38 39import com.android.internal.app.IBatteryStats; 40import com.android.internal.os.BatterySipper.DrainType; 41 42import java.io.File; 43import java.io.FileInputStream; 44import java.io.FileOutputStream; 45import java.io.IOException; 46import java.util.ArrayList; 47import java.util.Collections; 48import java.util.Comparator; 49import java.util.List; 50 51/** 52 * A helper class for retrieving the power usage information for all applications and services. 53 * 54 * The caller must initialize this class as soon as activity object is ready to use (for example, in 55 * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy(). 56 */ 57public final class BatteryStatsHelper { 58 static final boolean DEBUG = false; 59 60 private static final String TAG = BatteryStatsHelper.class.getSimpleName(); 61 62 private static BatteryStats sStatsXfer; 63 private static Intent sBatteryBroadcastXfer; 64 private static ArrayMap<File, BatteryStats> sFileXfer = new ArrayMap<>(); 65 66 final private Context mContext; 67 final private boolean mCollectBatteryBroadcast; 68 final private boolean mWifiOnly; 69 70 private IBatteryStats mBatteryInfo; 71 private BatteryStats mStats; 72 private Intent mBatteryBroadcast; 73 private PowerProfile mPowerProfile; 74 75 /** 76 * List of apps using power. 77 */ 78 private final List<BatterySipper> mUsageList = new ArrayList<>(); 79 80 /** 81 * List of apps using wifi power. 82 */ 83 private final List<BatterySipper> mWifiSippers = new ArrayList<>(); 84 85 /** 86 * List of apps using bluetooth power. 87 */ 88 private final List<BatterySipper> mBluetoothSippers = new ArrayList<>(); 89 90 private final SparseArray<List<BatterySipper>> mUserSippers = new SparseArray<>(); 91 92 private final List<BatterySipper> mMobilemsppList = new ArrayList<>(); 93 94 private int mStatsType = BatteryStats.STATS_SINCE_CHARGED; 95 96 long mRawRealtime; 97 long mRawUptime; 98 long mBatteryRealtime; 99 long mBatteryUptime; 100 long mTypeBatteryRealtime; 101 long mTypeBatteryUptime; 102 long mBatteryTimeRemaining; 103 long mChargeTimeRemaining; 104 105 private long mStatsPeriod = 0; 106 107 // The largest entry by power. 108 private double mMaxPower = 1; 109 110 // The largest real entry by power (not undercounted or overcounted). 111 private double mMaxRealPower = 1; 112 113 // Total computed power. 114 private double mComputedPower; 115 private double mTotalPower; 116 private double mMinDrainedPower; 117 private double mMaxDrainedPower; 118 119 PowerCalculator mCpuPowerCalculator; 120 PowerCalculator mWakelockPowerCalculator; 121 MobileRadioPowerCalculator mMobileRadioPowerCalculator; 122 PowerCalculator mWifiPowerCalculator; 123 PowerCalculator mBluetoothPowerCalculator; 124 PowerCalculator mSensorPowerCalculator; 125 PowerCalculator mCameraPowerCalculator; 126 PowerCalculator mFlashlightPowerCalculator; 127 128 public static boolean checkWifiOnly(Context context) { 129 ConnectivityManager cm = (ConnectivityManager)context.getSystemService( 130 Context.CONNECTIVITY_SERVICE); 131 return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); 132 } 133 134 public static boolean checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile) { 135 return stats.hasWifiActivityReporting() && 136 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE) != 0 && 137 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX) != 0 && 138 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0; 139 } 140 141 public static boolean checkHasBluetoothPowerReporting(BatteryStats stats, 142 PowerProfile profile) { 143 return stats.hasBluetoothActivityReporting() && 144 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE) != 0 && 145 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX) != 0 && 146 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX) != 0; 147 } 148 149 public BatteryStatsHelper(Context context) { 150 this(context, true); 151 } 152 153 public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) { 154 this(context, collectBatteryBroadcast, checkWifiOnly(context)); 155 } 156 157 public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) { 158 mContext = context; 159 mCollectBatteryBroadcast = collectBatteryBroadcast; 160 mWifiOnly = wifiOnly; 161 } 162 163 public void storeStatsHistoryInFile(String fname) { 164 synchronized (sFileXfer) { 165 File path = makeFilePath(mContext, fname); 166 sFileXfer.put(path, this.getStats()); 167 FileOutputStream fout = null; 168 try { 169 fout = new FileOutputStream(path); 170 Parcel hist = Parcel.obtain(); 171 getStats().writeToParcelWithoutUids(hist, 0); 172 byte[] histData = hist.marshall(); 173 fout.write(histData); 174 } catch (IOException e) { 175 Log.w(TAG, "Unable to write history to file", e); 176 } finally { 177 if (fout != null) { 178 try { 179 fout.close(); 180 } catch (IOException e) { 181 } 182 } 183 } 184 } 185 } 186 187 public static BatteryStats statsFromFile(Context context, String fname) { 188 synchronized (sFileXfer) { 189 File path = makeFilePath(context, fname); 190 BatteryStats stats = sFileXfer.get(path); 191 if (stats != null) { 192 return stats; 193 } 194 FileInputStream fin = null; 195 try { 196 fin = new FileInputStream(path); 197 byte[] data = readFully(fin); 198 Parcel parcel = Parcel.obtain(); 199 parcel.unmarshall(data, 0, data.length); 200 parcel.setDataPosition(0); 201 return com.android.internal.os.BatteryStatsImpl.CREATOR.createFromParcel(parcel); 202 } catch (IOException e) { 203 Log.w(TAG, "Unable to read history to file", e); 204 } finally { 205 if (fin != null) { 206 try { 207 fin.close(); 208 } catch (IOException e) { 209 } 210 } 211 } 212 } 213 return getStats(IBatteryStats.Stub.asInterface( 214 ServiceManager.getService(BatteryStats.SERVICE_NAME))); 215 } 216 217 public static void dropFile(Context context, String fname) { 218 makeFilePath(context, fname).delete(); 219 } 220 221 private static File makeFilePath(Context context, String fname) { 222 return new File(context.getFilesDir(), fname); 223 } 224 225 /** Clears the current stats and forces recreating for future use. */ 226 public void clearStats() { 227 mStats = null; 228 } 229 230 public BatteryStats getStats() { 231 if (mStats == null) { 232 load(); 233 } 234 return mStats; 235 } 236 237 public Intent getBatteryBroadcast() { 238 if (mBatteryBroadcast == null && mCollectBatteryBroadcast) { 239 load(); 240 } 241 return mBatteryBroadcast; 242 } 243 244 public PowerProfile getPowerProfile() { 245 return mPowerProfile; 246 } 247 248 public void create(BatteryStats stats) { 249 mPowerProfile = new PowerProfile(mContext); 250 mStats = stats; 251 } 252 253 public void create(Bundle icicle) { 254 if (icicle != null) { 255 mStats = sStatsXfer; 256 mBatteryBroadcast = sBatteryBroadcastXfer; 257 } 258 mBatteryInfo = IBatteryStats.Stub.asInterface( 259 ServiceManager.getService(BatteryStats.SERVICE_NAME)); 260 mPowerProfile = new PowerProfile(mContext); 261 } 262 263 public void storeState() { 264 sStatsXfer = mStats; 265 sBatteryBroadcastXfer = mBatteryBroadcast; 266 } 267 268 public static String makemAh(double power) { 269 if (power == 0) return "0"; 270 else if (power < .00001) return String.format("%.8f", power); 271 else if (power < .0001) return String.format("%.7f", power); 272 else if (power < .001) return String.format("%.6f", power); 273 else if (power < .01) return String.format("%.5f", power); 274 else if (power < .1) return String.format("%.4f", power); 275 else if (power < 1) return String.format("%.3f", power); 276 else if (power < 10) return String.format("%.2f", power); 277 else if (power < 100) return String.format("%.1f", power); 278 else return String.format("%.0f", power); 279 } 280 281 /** 282 * Refreshes the power usage list. 283 */ 284 public void refreshStats(int statsType, int asUser) { 285 SparseArray<UserHandle> users = new SparseArray<>(1); 286 users.put(asUser, new UserHandle(asUser)); 287 refreshStats(statsType, users); 288 } 289 290 /** 291 * Refreshes the power usage list. 292 */ 293 public void refreshStats(int statsType, List<UserHandle> asUsers) { 294 final int n = asUsers.size(); 295 SparseArray<UserHandle> users = new SparseArray<>(n); 296 for (int i = 0; i < n; ++i) { 297 UserHandle userHandle = asUsers.get(i); 298 users.put(userHandle.getIdentifier(), userHandle); 299 } 300 refreshStats(statsType, users); 301 } 302 303 /** 304 * Refreshes the power usage list. 305 */ 306 public void refreshStats(int statsType, SparseArray<UserHandle> asUsers) { 307 refreshStats(statsType, asUsers, SystemClock.elapsedRealtime() * 1000, 308 SystemClock.uptimeMillis() * 1000); 309 } 310 311 public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs, 312 long rawUptimeUs) { 313 // Initialize mStats if necessary. 314 getStats(); 315 316 mMaxPower = 0; 317 mMaxRealPower = 0; 318 mComputedPower = 0; 319 mTotalPower = 0; 320 321 mUsageList.clear(); 322 mWifiSippers.clear(); 323 mBluetoothSippers.clear(); 324 mUserSippers.clear(); 325 mMobilemsppList.clear(); 326 327 if (mStats == null) { 328 return; 329 } 330 331 if (mCpuPowerCalculator == null) { 332 mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile); 333 } 334 mCpuPowerCalculator.reset(); 335 336 if (mWakelockPowerCalculator == null) { 337 mWakelockPowerCalculator = new WakelockPowerCalculator(mPowerProfile); 338 } 339 mWakelockPowerCalculator.reset(); 340 341 if (mMobileRadioPowerCalculator == null) { 342 mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile, mStats); 343 } 344 mMobileRadioPowerCalculator.reset(mStats); 345 346 if (mWifiPowerCalculator == null) { 347 if (checkHasWifiPowerReporting(mStats, mPowerProfile)) { 348 mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile); 349 } else { 350 mWifiPowerCalculator = new WifiPowerEstimator(mPowerProfile); 351 } 352 } 353 mWifiPowerCalculator.reset(); 354 355 if (mBluetoothPowerCalculator == null) { 356 if (checkHasBluetoothPowerReporting(mStats, mPowerProfile)) { 357 mBluetoothPowerCalculator = new BluetoothPowerCalculator(); 358 } else { 359 mBluetoothPowerCalculator = new BluetoothPowerCalculator(); 360 } 361 } 362 mBluetoothPowerCalculator.reset(); 363 364 if (mSensorPowerCalculator == null) { 365 mSensorPowerCalculator = new SensorPowerCalculator(mPowerProfile, 366 (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE)); 367 } 368 mSensorPowerCalculator.reset(); 369 370 if (mCameraPowerCalculator == null) { 371 mCameraPowerCalculator = new CameraPowerCalculator(mPowerProfile); 372 } 373 mCameraPowerCalculator.reset(); 374 375 if (mFlashlightPowerCalculator == null) { 376 mFlashlightPowerCalculator = new FlashlightPowerCalculator(mPowerProfile); 377 } 378 mFlashlightPowerCalculator.reset(); 379 380 mStatsType = statsType; 381 mRawUptime = rawUptimeUs; 382 mRawRealtime = rawRealtimeUs; 383 mBatteryUptime = mStats.getBatteryUptime(rawUptimeUs); 384 mBatteryRealtime = mStats.getBatteryRealtime(rawRealtimeUs); 385 mTypeBatteryUptime = mStats.computeBatteryUptime(rawUptimeUs, mStatsType); 386 mTypeBatteryRealtime = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType); 387 mBatteryTimeRemaining = mStats.computeBatteryTimeRemaining(rawRealtimeUs); 388 mChargeTimeRemaining = mStats.computeChargeTimeRemaining(rawRealtimeUs); 389 390 if (DEBUG) { 391 Log.d(TAG, "Raw time: realtime=" + (rawRealtimeUs/1000) + " uptime=" 392 + (rawUptimeUs/1000)); 393 Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtime/1000) + " uptime=" 394 + (mBatteryUptime/1000)); 395 Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtime/1000) + " uptime=" 396 + (mTypeBatteryUptime/1000)); 397 } 398 mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge() 399 * mPowerProfile.getBatteryCapacity()) / 100; 400 mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge() 401 * mPowerProfile.getBatteryCapacity()) / 100; 402 403 processAppUsage(asUsers); 404 405 // Before aggregating apps in to users, collect all apps to sort by their ms per packet. 406 for (int i=0; i<mUsageList.size(); i++) { 407 BatterySipper bs = mUsageList.get(i); 408 bs.computeMobilemspp(); 409 if (bs.mobilemspp != 0) { 410 mMobilemsppList.add(bs); 411 } 412 } 413 414 for (int i=0; i<mUserSippers.size(); i++) { 415 List<BatterySipper> user = mUserSippers.valueAt(i); 416 for (int j=0; j<user.size(); j++) { 417 BatterySipper bs = user.get(j); 418 bs.computeMobilemspp(); 419 if (bs.mobilemspp != 0) { 420 mMobilemsppList.add(bs); 421 } 422 } 423 } 424 Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() { 425 @Override 426 public int compare(BatterySipper lhs, BatterySipper rhs) { 427 return Double.compare(rhs.mobilemspp, lhs.mobilemspp); 428 } 429 }); 430 431 processMiscUsage(); 432 433 Collections.sort(mUsageList); 434 435 // At this point, we've sorted the list so we are guaranteed the max values are at the top. 436 // We have only added real powers so far. 437 if (!mUsageList.isEmpty()) { 438 mMaxRealPower = mMaxPower = mUsageList.get(0).totalPowerMah; 439 final int usageListCount = mUsageList.size(); 440 for (int i = 0; i < usageListCount; i++) { 441 mComputedPower += mUsageList.get(i).totalPowerMah; 442 } 443 } 444 445 if (DEBUG) { 446 Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge=" 447 + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower)); 448 } 449 450 mTotalPower = mComputedPower; 451 if (mStats.getLowDischargeAmountSinceCharge() > 1) { 452 if (mMinDrainedPower > mComputedPower) { 453 double amount = mMinDrainedPower - mComputedPower; 454 mTotalPower = mMinDrainedPower; 455 BatterySipper bs = new BatterySipper(DrainType.UNACCOUNTED, null, amount); 456 457 // Insert the BatterySipper in its sorted position. 458 int index = Collections.binarySearch(mUsageList, bs); 459 if (index < 0) { 460 index = -(index + 1); 461 } 462 mUsageList.add(index, bs); 463 mMaxPower = Math.max(mMaxPower, amount); 464 } else if (mMaxDrainedPower < mComputedPower) { 465 double amount = mComputedPower - mMaxDrainedPower; 466 467 // Insert the BatterySipper in its sorted position. 468 BatterySipper bs = new BatterySipper(DrainType.OVERCOUNTED, null, amount); 469 int index = Collections.binarySearch(mUsageList, bs); 470 if (index < 0) { 471 index = -(index + 1); 472 } 473 mUsageList.add(index, bs); 474 mMaxPower = Math.max(mMaxPower, amount); 475 } 476 } 477 } 478 479 private void processAppUsage(SparseArray<UserHandle> asUsers) { 480 final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null); 481 mStatsPeriod = mTypeBatteryRealtime; 482 483 final SparseArray<? extends Uid> uidStats = mStats.getUidStats(); 484 final int NU = uidStats.size(); 485 for (int iu = 0; iu < NU; iu++) { 486 final Uid u = uidStats.valueAt(iu); 487 final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0); 488 489 mCpuPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); 490 mWakelockPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); 491 mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); 492 mWifiPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); 493 mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); 494 mSensorPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); 495 mCameraPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); 496 mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); 497 498 final double totalPower = app.sumPower(); 499 if (DEBUG && totalPower != 0) { 500 Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(), 501 makemAh(totalPower))); 502 } 503 504 // Add the app to the list if it is consuming power. 505 if (totalPower != 0 || u.getUid() == 0) { 506 // 507 // Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list. 508 // 509 final int uid = app.getUid(); 510 final int userId = UserHandle.getUserId(uid); 511 if (uid == Process.WIFI_UID) { 512 mWifiSippers.add(app); 513 } else if (uid == Process.BLUETOOTH_UID) { 514 mBluetoothSippers.add(app); 515 } else if (!forAllUsers && asUsers.get(userId) == null 516 && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) { 517 // We are told to just report this user's apps as one large entry. 518 List<BatterySipper> list = mUserSippers.get(userId); 519 if (list == null) { 520 list = new ArrayList<>(); 521 mUserSippers.put(userId, list); 522 } 523 list.add(app); 524 } else { 525 mUsageList.add(app); 526 } 527 528 if (uid == 0) { 529 // The device has probably been awake for longer than the screen on 530 // time and application wake lock time would account for. Assign 531 // this remainder to the OS, if possible. 532 mWakelockPowerCalculator.calculateRemaining(app, mStats, mRawRealtime, 533 mRawUptime, mStatsType); 534 app.sumPower(); 535 } 536 } 537 } 538 } 539 540 private void addPhoneUsage() { 541 long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtime, mStatsType) / 1000; 542 double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) 543 * phoneOnTimeMs / (60*60*1000); 544 if (phoneOnPower != 0) { 545 addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower); 546 } 547 } 548 549 private void addScreenUsage() { 550 double power = 0; 551 long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtime, mStatsType) / 1000; 552 power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON); 553 final double screenFullPower = 554 mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL); 555 for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) { 556 double screenBinPower = screenFullPower * (i + 0.5f) 557 / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; 558 long brightnessTime = mStats.getScreenBrightnessTime(i, mRawRealtime, mStatsType) 559 / 1000; 560 double p = screenBinPower*brightnessTime; 561 if (DEBUG && p != 0) { 562 Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime 563 + " power=" + makemAh(p / (60 * 60 * 1000))); 564 } 565 power += p; 566 } 567 power /= (60*60*1000); // To hours 568 if (power != 0) { 569 addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power); 570 } 571 } 572 573 private void addRadioUsage() { 574 BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0); 575 mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtime, mRawUptime, 576 mStatsType); 577 radio.sumPower(); 578 if (radio.totalPowerMah > 0) { 579 mUsageList.add(radio); 580 } 581 } 582 583 private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) { 584 for (int i=0; i<from.size(); i++) { 585 BatterySipper wbs = from.get(i); 586 if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTimeMs); 587 bs.add(wbs); 588 } 589 bs.computeMobilemspp(); 590 } 591 592 private void addIdleUsage() { 593 long idleTimeMs = (mTypeBatteryRealtime 594 - mStats.getScreenOnTime(mRawRealtime, mStatsType)) / 1000; 595 double idlePower = (idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE)) 596 / (60*60*1000); 597 if (DEBUG && idlePower != 0) { 598 Log.d(TAG, "Idle: time=" + idleTimeMs + " power=" + makemAh(idlePower)); 599 } 600 if (idlePower != 0) { 601 addEntry(BatterySipper.DrainType.IDLE, idleTimeMs, idlePower); 602 } 603 } 604 605 /** 606 * We do per-app blaming of WiFi activity. If energy info is reported from the controller, 607 * then only the WiFi process gets blamed here since we normalize power calculations and 608 * assign all the power drain to apps. If energy info is not reported, we attribute the 609 * difference between total running time of WiFi for all apps and the actual running time 610 * of WiFi to the WiFi subsystem. 611 */ 612 private void addWiFiUsage() { 613 BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0); 614 mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, mStatsType); 615 bs.sumPower(); 616 if (bs.totalPowerMah > 0 || !mWifiSippers.isEmpty()) { 617 aggregateSippers(bs, mWifiSippers, "WIFI"); 618 mUsageList.add(bs); 619 } 620 } 621 622 /** 623 * Bluetooth usage is not attributed to any apps yet, so the entire blame goes to the 624 * Bluetooth Category. 625 */ 626 private void addBluetoothUsage() { 627 BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0); 628 mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, 629 mStatsType); 630 if (bs.sumPower() > 0) { 631 aggregateSippers(bs, mBluetoothSippers, "Bluetooth"); 632 mUsageList.add(bs); 633 } 634 } 635 636 private void addUserUsage() { 637 for (int i = 0; i < mUserSippers.size(); i++) { 638 final int userId = mUserSippers.keyAt(i); 639 BatterySipper bs = new BatterySipper(DrainType.USER, null, 0); 640 bs.userId = userId; 641 aggregateSippers(bs, mUserSippers.valueAt(i), "User"); 642 bs.sumPower(); 643 mUsageList.add(bs); 644 } 645 } 646 647 private void processMiscUsage() { 648 addUserUsage(); 649 addPhoneUsage(); 650 addScreenUsage(); 651 addWiFiUsage(); 652 addBluetoothUsage(); 653 addIdleUsage(); // Not including cellular idle power 654 // Don't compute radio usage if it's a wifi-only device 655 if (!mWifiOnly) { 656 addRadioUsage(); 657 } 658 } 659 660 private BatterySipper addEntry(DrainType drainType, long time, double power) { 661 BatterySipper bs = new BatterySipper(drainType, null, 0); 662 bs.usagePowerMah = power; 663 bs.usageTimeMs = time; 664 bs.sumPower(); 665 mUsageList.add(bs); 666 return bs; 667 } 668 669 public List<BatterySipper> getUsageList() { 670 return mUsageList; 671 } 672 673 public List<BatterySipper> getMobilemsppList() { 674 return mMobilemsppList; 675 } 676 677 public long getStatsPeriod() { return mStatsPeriod; } 678 679 public int getStatsType() { return mStatsType; } 680 681 public double getMaxPower() { return mMaxPower; } 682 683 public double getMaxRealPower() { return mMaxRealPower; } 684 685 public double getTotalPower() { return mTotalPower; } 686 687 public double getComputedPower() { return mComputedPower; } 688 689 public double getMinDrainedPower() { 690 return mMinDrainedPower; 691 } 692 693 public double getMaxDrainedPower() { 694 return mMaxDrainedPower; 695 } 696 697 public long getBatteryTimeRemaining() { return mBatteryTimeRemaining; } 698 699 public long getChargeTimeRemaining() { return mChargeTimeRemaining; } 700 701 public static byte[] readFully(FileInputStream stream) throws java.io.IOException { 702 return readFully(stream, stream.available()); 703 } 704 705 public static byte[] readFully(FileInputStream stream, int avail) throws java.io.IOException { 706 int pos = 0; 707 byte[] data = new byte[avail]; 708 while (true) { 709 int amt = stream.read(data, pos, data.length-pos); 710 //Log.i("foo", "Read " + amt + " bytes at " + pos 711 // + " of avail " + data.length); 712 if (amt <= 0) { 713 //Log.i("foo", "**** FINISHED READING: pos=" + pos 714 // + " len=" + data.length); 715 return data; 716 } 717 pos += amt; 718 avail = stream.available(); 719 if (avail > data.length-pos) { 720 byte[] newData = new byte[pos+avail]; 721 System.arraycopy(data, 0, newData, 0, pos); 722 data = newData; 723 } 724 } 725 } 726 727 private void load() { 728 if (mBatteryInfo == null) { 729 return; 730 } 731 mStats = getStats(mBatteryInfo); 732 if (mCollectBatteryBroadcast) { 733 mBatteryBroadcast = mContext.registerReceiver(null, 734 new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 735 } 736 } 737 738 private static BatteryStatsImpl getStats(IBatteryStats service) { 739 try { 740 ParcelFileDescriptor pfd = service.getStatisticsStream(); 741 if (pfd != null) { 742 FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd); 743 try { 744 byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor())); 745 Parcel parcel = Parcel.obtain(); 746 parcel.unmarshall(data, 0, data.length); 747 parcel.setDataPosition(0); 748 BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR 749 .createFromParcel(parcel); 750 return stats; 751 } catch (IOException e) { 752 Log.w(TAG, "Unable to read statistics stream", e); 753 } 754 } 755 } catch (RemoteException e) { 756 Log.w(TAG, "RemoteException:", e); 757 } 758 return new BatteryStatsImpl(); 759 } 760} 761