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 mRawRealtimeUs; 98 long mRawUptimeUs; 99 long mBatteryRealtimeUs; 100 long mBatteryUptimeUs; 101 long mTypeBatteryRealtimeUs; 102 long mTypeBatteryUptimeUs; 103 long mBatteryTimeRemainingUs; 104 long mChargeTimeRemainingUs; 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 mRawUptimeUs = rawUptimeUs; 393 mRawRealtimeUs = rawRealtimeUs; 394 mBatteryUptimeUs = mStats.getBatteryUptime(rawUptimeUs); 395 mBatteryRealtimeUs = mStats.getBatteryRealtime(rawRealtimeUs); 396 mTypeBatteryUptimeUs = mStats.computeBatteryUptime(rawUptimeUs, mStatsType); 397 mTypeBatteryRealtimeUs = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType); 398 mBatteryTimeRemainingUs = mStats.computeBatteryTimeRemaining(rawRealtimeUs); 399 mChargeTimeRemainingUs = 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=" + (mBatteryRealtimeUs /1000) + " uptime=" 405 + (mBatteryUptimeUs /1000)); 406 Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtimeUs /1000) + " uptime=" 407 + (mTypeBatteryUptimeUs /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 = mTypeBatteryRealtimeUs; 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, mRawRealtimeUs, mRawUptimeUs, mStatsType); 502 mWakelockPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); 503 mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); 504 mWifiPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); 505 mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); 506 mSensorPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); 507 mCameraPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); 508 mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, 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, mRawRealtimeUs, 551 mRawUptimeUs, mStatsType); 552 osSipper.sumPower(); 553 } 554 } 555 addPhoneUsage()556 private void addPhoneUsage() { 557 long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtimeUs, 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 565 /** 566 * Screen power is the additional power the screen takes while the device is running. 567 */ addScreenUsage()568 private void addScreenUsage() { 569 double power = 0; 570 long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtimeUs, mStatsType) / 1000; 571 power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON); 572 final double screenFullPower = 573 mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL); 574 for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) { 575 double screenBinPower = screenFullPower * (i + 0.5f) 576 / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; 577 long brightnessTime = mStats.getScreenBrightnessTime(i, mRawRealtimeUs, mStatsType) 578 / 1000; 579 double p = screenBinPower*brightnessTime; 580 if (DEBUG && p != 0) { 581 Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime 582 + " power=" + makemAh(p / (60 * 60 * 1000))); 583 } 584 power += p; 585 } 586 power /= (60*60*1000); // To hours 587 if (power != 0) { 588 addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power); 589 } 590 } 591 addRadioUsage()592 private void addRadioUsage() { 593 BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0); 594 mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtimeUs, mRawUptimeUs, 595 mStatsType); 596 radio.sumPower(); 597 if (radio.totalPowerMah > 0) { 598 mUsageList.add(radio); 599 } 600 } 601 aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag)602 private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) { 603 for (int i=0; i<from.size(); i++) { 604 BatterySipper wbs = from.get(i); 605 if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTimeMs); 606 bs.add(wbs); 607 } 608 bs.computeMobilemspp(); 609 bs.sumPower(); 610 } 611 612 /** 613 * Calculate the baseline power usage for the device when it is in suspend and idle. 614 * The device is drawing POWER_CPU_IDLE power at its lowest power state. 615 * The device is drawing POWER_CPU_IDLE + POWER_CPU_AWAKE power when a wakelock is held. 616 */ addIdleUsage()617 private void addIdleUsage() { 618 final double suspendPowerMaMs = (mTypeBatteryRealtimeUs / 1000) * 619 mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE); 620 final double idlePowerMaMs = (mTypeBatteryUptimeUs / 1000) * 621 mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE); 622 final double totalPowerMah = (suspendPowerMaMs + idlePowerMaMs) / (60 * 60 * 1000); 623 if (DEBUG && totalPowerMah != 0) { 624 Log.d(TAG, "Suspend: time=" + (mTypeBatteryRealtimeUs / 1000) 625 + " power=" + makemAh(suspendPowerMaMs / (60 * 60 * 1000))); 626 Log.d(TAG, "Idle: time=" + (mTypeBatteryUptimeUs / 1000) 627 + " power=" + makemAh(idlePowerMaMs / (60 * 60 * 1000))); 628 } 629 630 if (totalPowerMah != 0) { 631 addEntry(BatterySipper.DrainType.IDLE, mTypeBatteryRealtimeUs / 1000, totalPowerMah); 632 } 633 } 634 635 /** 636 * We do per-app blaming of WiFi activity. If energy info is reported from the controller, 637 * then only the WiFi process gets blamed here since we normalize power calculations and 638 * assign all the power drain to apps. If energy info is not reported, we attribute the 639 * difference between total running time of WiFi for all apps and the actual running time 640 * of WiFi to the WiFi subsystem. 641 */ addWiFiUsage()642 private void addWiFiUsage() { 643 BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0); 644 mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtimeUs, mRawUptimeUs, mStatsType); 645 aggregateSippers(bs, mWifiSippers, "WIFI"); 646 if (bs.totalPowerMah > 0) { 647 mUsageList.add(bs); 648 } 649 } 650 651 /** 652 * Bluetooth usage is not attributed to any apps yet, so the entire blame goes to the 653 * Bluetooth Category. 654 */ addBluetoothUsage()655 private void addBluetoothUsage() { 656 BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0); 657 mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtimeUs, mRawUptimeUs, 658 mStatsType); 659 aggregateSippers(bs, mBluetoothSippers, "Bluetooth"); 660 if (bs.totalPowerMah > 0) { 661 mUsageList.add(bs); 662 } 663 } 664 addUserUsage()665 private void addUserUsage() { 666 for (int i = 0; i < mUserSippers.size(); i++) { 667 final int userId = mUserSippers.keyAt(i); 668 BatterySipper bs = new BatterySipper(DrainType.USER, null, 0); 669 bs.userId = userId; 670 aggregateSippers(bs, mUserSippers.valueAt(i), "User"); 671 mUsageList.add(bs); 672 } 673 } 674 processMiscUsage()675 private void processMiscUsage() { 676 addUserUsage(); 677 addPhoneUsage(); 678 addScreenUsage(); 679 addWiFiUsage(); 680 addBluetoothUsage(); 681 addIdleUsage(); // Not including cellular idle power 682 // Don't compute radio usage if it's a wifi-only device 683 if (!mWifiOnly) { 684 addRadioUsage(); 685 } 686 } 687 addEntry(DrainType drainType, long time, double power)688 private BatterySipper addEntry(DrainType drainType, long time, double power) { 689 BatterySipper bs = new BatterySipper(drainType, null, 0); 690 bs.usagePowerMah = power; 691 bs.usageTimeMs = time; 692 bs.sumPower(); 693 mUsageList.add(bs); 694 return bs; 695 } 696 getUsageList()697 public List<BatterySipper> getUsageList() { 698 return mUsageList; 699 } 700 getMobilemsppList()701 public List<BatterySipper> getMobilemsppList() { 702 return mMobilemsppList; 703 } 704 getStatsPeriod()705 public long getStatsPeriod() { return mStatsPeriod; } 706 getStatsType()707 public int getStatsType() { return mStatsType; } 708 getMaxPower()709 public double getMaxPower() { return mMaxPower; } 710 getMaxRealPower()711 public double getMaxRealPower() { return mMaxRealPower; } 712 getTotalPower()713 public double getTotalPower() { return mTotalPower; } 714 getComputedPower()715 public double getComputedPower() { return mComputedPower; } 716 getMinDrainedPower()717 public double getMinDrainedPower() { 718 return mMinDrainedPower; 719 } 720 getMaxDrainedPower()721 public double getMaxDrainedPower() { 722 return mMaxDrainedPower; 723 } 724 readFully(FileInputStream stream)725 public static byte[] readFully(FileInputStream stream) throws java.io.IOException { 726 return readFully(stream, stream.available()); 727 } 728 readFully(FileInputStream stream, int avail)729 public static byte[] readFully(FileInputStream stream, int avail) throws java.io.IOException { 730 int pos = 0; 731 byte[] data = new byte[avail]; 732 while (true) { 733 int amt = stream.read(data, pos, data.length-pos); 734 //Log.i("foo", "Read " + amt + " bytes at " + pos 735 // + " of avail " + data.length); 736 if (amt <= 0) { 737 //Log.i("foo", "**** FINISHED READING: pos=" + pos 738 // + " len=" + data.length); 739 return data; 740 } 741 pos += amt; 742 avail = stream.available(); 743 if (avail > data.length-pos) { 744 byte[] newData = new byte[pos+avail]; 745 System.arraycopy(data, 0, newData, 0, pos); 746 data = newData; 747 } 748 } 749 } 750 load()751 private void load() { 752 if (mBatteryInfo == null) { 753 return; 754 } 755 mStats = getStats(mBatteryInfo); 756 if (mCollectBatteryBroadcast) { 757 mBatteryBroadcast = mContext.registerReceiver(null, 758 new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 759 } 760 } 761 getStats(IBatteryStats service)762 private static BatteryStatsImpl getStats(IBatteryStats service) { 763 try { 764 ParcelFileDescriptor pfd = service.getStatisticsStream(); 765 if (pfd != null) { 766 try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) { 767 byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor())); 768 Parcel parcel = Parcel.obtain(); 769 parcel.unmarshall(data, 0, data.length); 770 parcel.setDataPosition(0); 771 BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR 772 .createFromParcel(parcel); 773 return stats; 774 } catch (IOException e) { 775 Log.w(TAG, "Unable to read statistics stream", e); 776 } 777 } 778 } catch (RemoteException e) { 779 Log.w(TAG, "RemoteException:", e); 780 } 781 return new BatteryStatsImpl(); 782 } 783 } 784