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