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.content.pm.PackageManager; 23 import android.content.res.Resources; 24 import android.hardware.SensorManager; 25 import android.net.ConnectivityManager; 26 import android.os.BatteryStats; 27 import android.os.BatteryStats.Uid; 28 import android.os.Bundle; 29 import android.os.MemoryFile; 30 import android.os.Parcel; 31 import android.os.ParcelFileDescriptor; 32 import android.os.Process; 33 import android.os.RemoteException; 34 import android.os.ServiceManager; 35 import android.os.SystemClock; 36 import android.os.UserHandle; 37 import android.text.format.DateUtils; 38 import android.util.ArrayMap; 39 import android.util.Log; 40 import android.util.SparseArray; 41 import android.util.SparseLongArray; 42 43 import com.android.internal.annotations.VisibleForTesting; 44 import com.android.internal.app.IBatteryStats; 45 import com.android.internal.os.BatterySipper.DrainType; 46 import com.android.internal.util.ArrayUtils; 47 48 import java.io.File; 49 import java.io.FileInputStream; 50 import java.io.FileOutputStream; 51 import java.io.IOException; 52 import java.util.ArrayList; 53 import java.util.Collections; 54 import java.util.Comparator; 55 import java.util.List; 56 import java.util.Locale; 57 58 /** 59 * A helper class for retrieving the power usage information for all applications and services. 60 * 61 * The caller must initialize this class as soon as activity object is ready to use (for example, in 62 * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy(). 63 */ 64 public class BatteryStatsHelper { 65 static final boolean DEBUG = false; 66 67 private static final String TAG = BatteryStatsHelper.class.getSimpleName(); 68 69 private static BatteryStats sStatsXfer; 70 private static Intent sBatteryBroadcastXfer; 71 private static ArrayMap<File, BatteryStats> sFileXfer = new ArrayMap<>(); 72 73 final private Context mContext; 74 final private boolean mCollectBatteryBroadcast; 75 final private boolean mWifiOnly; 76 77 private IBatteryStats mBatteryInfo; 78 private BatteryStats mStats; 79 private Intent mBatteryBroadcast; 80 private PowerProfile mPowerProfile; 81 82 private String[] mSystemPackageArray; 83 private String[] mServicepackageArray; 84 private PackageManager mPackageManager; 85 86 /** 87 * List of apps using power. 88 */ 89 private final List<BatterySipper> mUsageList = new ArrayList<>(); 90 91 /** 92 * List of apps using wifi power. 93 */ 94 private final List<BatterySipper> mWifiSippers = new ArrayList<>(); 95 96 /** 97 * List of apps using bluetooth power. 98 */ 99 private final List<BatterySipper> mBluetoothSippers = new ArrayList<>(); 100 101 private final SparseArray<List<BatterySipper>> mUserSippers = new SparseArray<>(); 102 103 private final List<BatterySipper> mMobilemsppList = new ArrayList<>(); 104 105 private int mStatsType = BatteryStats.STATS_SINCE_CHARGED; 106 107 long mRawRealtimeUs; 108 long mRawUptimeUs; 109 long mBatteryRealtimeUs; 110 long mBatteryUptimeUs; 111 long mTypeBatteryRealtimeUs; 112 long mTypeBatteryUptimeUs; 113 long mBatteryTimeRemainingUs; 114 long mChargeTimeRemainingUs; 115 116 private long mStatsPeriod = 0; 117 118 // The largest entry by power. 119 private double mMaxPower = 1; 120 121 // The largest real entry by power (not undercounted or overcounted). 122 private double mMaxRealPower = 1; 123 124 // Total computed power. 125 private double mComputedPower; 126 private double mTotalPower; 127 private double mMinDrainedPower; 128 private double mMaxDrainedPower; 129 130 PowerCalculator mCpuPowerCalculator; 131 PowerCalculator mWakelockPowerCalculator; 132 MobileRadioPowerCalculator mMobileRadioPowerCalculator; 133 PowerCalculator mWifiPowerCalculator; 134 PowerCalculator mBluetoothPowerCalculator; 135 PowerCalculator mSensorPowerCalculator; 136 PowerCalculator mCameraPowerCalculator; 137 PowerCalculator mFlashlightPowerCalculator; 138 PowerCalculator mMemoryPowerCalculator; 139 140 boolean mHasWifiPowerReporting = false; 141 boolean mHasBluetoothPowerReporting = false; 142 checkWifiOnly(Context context)143 public static boolean checkWifiOnly(Context context) { 144 ConnectivityManager cm = (ConnectivityManager) context.getSystemService( 145 Context.CONNECTIVITY_SERVICE); 146 return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); 147 } 148 checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile)149 public static boolean checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile) { 150 return stats.hasWifiActivityReporting() && 151 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE) != 0 && 152 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX) != 0 && 153 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0; 154 } 155 checkHasBluetoothPowerReporting(BatteryStats stats, PowerProfile profile)156 public static boolean checkHasBluetoothPowerReporting(BatteryStats stats, 157 PowerProfile profile) { 158 return stats.hasBluetoothActivityReporting() && 159 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE) != 0 && 160 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX) != 0 && 161 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX) != 0; 162 } 163 BatteryStatsHelper(Context context)164 public BatteryStatsHelper(Context context) { 165 this(context, true); 166 } 167 BatteryStatsHelper(Context context, boolean collectBatteryBroadcast)168 public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) { 169 this(context, collectBatteryBroadcast, checkWifiOnly(context)); 170 } 171 BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly)172 public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) { 173 mContext = context; 174 mCollectBatteryBroadcast = collectBatteryBroadcast; 175 mWifiOnly = wifiOnly; 176 mPackageManager = context.getPackageManager(); 177 178 final Resources resources = context.getResources(); 179 mSystemPackageArray = resources.getStringArray( 180 com.android.internal.R.array.config_batteryPackageTypeSystem); 181 mServicepackageArray = resources.getStringArray( 182 com.android.internal.R.array.config_batteryPackageTypeService); 183 } 184 storeStatsHistoryInFile(String fname)185 public void storeStatsHistoryInFile(String fname) { 186 synchronized (sFileXfer) { 187 File path = makeFilePath(mContext, fname); 188 sFileXfer.put(path, this.getStats()); 189 FileOutputStream fout = null; 190 try { 191 fout = new FileOutputStream(path); 192 Parcel hist = Parcel.obtain(); 193 getStats().writeToParcelWithoutUids(hist, 0); 194 byte[] histData = hist.marshall(); 195 fout.write(histData); 196 } catch (IOException e) { 197 Log.w(TAG, "Unable to write history to file", e); 198 } finally { 199 if (fout != null) { 200 try { 201 fout.close(); 202 } catch (IOException e) { 203 } 204 } 205 } 206 } 207 } 208 statsFromFile(Context context, String fname)209 public static BatteryStats statsFromFile(Context context, String fname) { 210 synchronized (sFileXfer) { 211 File path = makeFilePath(context, fname); 212 BatteryStats stats = sFileXfer.get(path); 213 if (stats != null) { 214 return stats; 215 } 216 FileInputStream fin = null; 217 try { 218 fin = new FileInputStream(path); 219 byte[] data = readFully(fin); 220 Parcel parcel = Parcel.obtain(); 221 parcel.unmarshall(data, 0, data.length); 222 parcel.setDataPosition(0); 223 return com.android.internal.os.BatteryStatsImpl.CREATOR.createFromParcel(parcel); 224 } catch (IOException e) { 225 Log.w(TAG, "Unable to read history to file", e); 226 } finally { 227 if (fin != null) { 228 try { 229 fin.close(); 230 } catch (IOException e) { 231 } 232 } 233 } 234 } 235 return getStats(IBatteryStats.Stub.asInterface( 236 ServiceManager.getService(BatteryStats.SERVICE_NAME))); 237 } 238 dropFile(Context context, String fname)239 public static void dropFile(Context context, String fname) { 240 makeFilePath(context, fname).delete(); 241 } 242 makeFilePath(Context context, String fname)243 private static File makeFilePath(Context context, String fname) { 244 return new File(context.getFilesDir(), fname); 245 } 246 247 /** Clears the current stats and forces recreating for future use. */ clearStats()248 public void clearStats() { 249 mStats = null; 250 } 251 getStats()252 public BatteryStats getStats() { 253 if (mStats == null) { 254 load(); 255 } 256 return mStats; 257 } 258 getBatteryBroadcast()259 public Intent getBatteryBroadcast() { 260 if (mBatteryBroadcast == null && mCollectBatteryBroadcast) { 261 load(); 262 } 263 return mBatteryBroadcast; 264 } 265 getPowerProfile()266 public PowerProfile getPowerProfile() { 267 return mPowerProfile; 268 } 269 create(BatteryStats stats)270 public void create(BatteryStats stats) { 271 mPowerProfile = new PowerProfile(mContext); 272 mStats = stats; 273 } 274 create(Bundle icicle)275 public void create(Bundle icicle) { 276 if (icicle != null) { 277 mStats = sStatsXfer; 278 mBatteryBroadcast = sBatteryBroadcastXfer; 279 } 280 mBatteryInfo = IBatteryStats.Stub.asInterface( 281 ServiceManager.getService(BatteryStats.SERVICE_NAME)); 282 mPowerProfile = new PowerProfile(mContext); 283 } 284 storeState()285 public void storeState() { 286 sStatsXfer = mStats; 287 sBatteryBroadcastXfer = mBatteryBroadcast; 288 } 289 makemAh(double power)290 public static String makemAh(double power) { 291 if (power == 0) return "0"; 292 293 final String format; 294 if (power < .00001) { 295 format = "%.8f"; 296 } else if (power < .0001) { 297 format = "%.7f"; 298 } else if (power < .001) { 299 format = "%.6f"; 300 } else if (power < .01) { 301 format = "%.5f"; 302 } else if (power < .1) { 303 format = "%.4f"; 304 } else if (power < 1) { 305 format = "%.3f"; 306 } else if (power < 10) { 307 format = "%.2f"; 308 } else if (power < 100) { 309 format = "%.1f"; 310 } else { 311 format = "%.0f"; 312 } 313 314 // Use English locale because this is never used in UI (only in checkin and dump). 315 return String.format(Locale.ENGLISH, format, power); 316 } 317 318 /** 319 * Refreshes the power usage list. 320 */ refreshStats(int statsType, int asUser)321 public void refreshStats(int statsType, int asUser) { 322 SparseArray<UserHandle> users = new SparseArray<>(1); 323 users.put(asUser, new UserHandle(asUser)); 324 refreshStats(statsType, users); 325 } 326 327 /** 328 * Refreshes the power usage list. 329 */ refreshStats(int statsType, List<UserHandle> asUsers)330 public void refreshStats(int statsType, List<UserHandle> asUsers) { 331 final int n = asUsers.size(); 332 SparseArray<UserHandle> users = new SparseArray<>(n); 333 for (int i = 0; i < n; ++i) { 334 UserHandle userHandle = asUsers.get(i); 335 users.put(userHandle.getIdentifier(), userHandle); 336 } 337 refreshStats(statsType, users); 338 } 339 340 /** 341 * Refreshes the power usage list. 342 */ refreshStats(int statsType, SparseArray<UserHandle> asUsers)343 public void refreshStats(int statsType, SparseArray<UserHandle> asUsers) { 344 refreshStats(statsType, asUsers, SystemClock.elapsedRealtime() * 1000, 345 SystemClock.uptimeMillis() * 1000); 346 } 347 refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs, long rawUptimeUs)348 public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs, 349 long rawUptimeUs) { 350 // Initialize mStats if necessary. 351 getStats(); 352 353 mMaxPower = 0; 354 mMaxRealPower = 0; 355 mComputedPower = 0; 356 mTotalPower = 0; 357 358 mUsageList.clear(); 359 mWifiSippers.clear(); 360 mBluetoothSippers.clear(); 361 mUserSippers.clear(); 362 mMobilemsppList.clear(); 363 364 if (mStats == null) { 365 return; 366 } 367 368 if (mCpuPowerCalculator == null) { 369 mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile); 370 } 371 mCpuPowerCalculator.reset(); 372 373 if (mMemoryPowerCalculator == null) { 374 mMemoryPowerCalculator = new MemoryPowerCalculator(mPowerProfile); 375 } 376 mMemoryPowerCalculator.reset(); 377 378 if (mWakelockPowerCalculator == null) { 379 mWakelockPowerCalculator = new WakelockPowerCalculator(mPowerProfile); 380 } 381 mWakelockPowerCalculator.reset(); 382 383 if (mMobileRadioPowerCalculator == null) { 384 mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile, mStats); 385 } 386 mMobileRadioPowerCalculator.reset(mStats); 387 388 // checkHasWifiPowerReporting can change if we get energy data at a later point, so 389 // always check this field. 390 final boolean hasWifiPowerReporting = checkHasWifiPowerReporting(mStats, mPowerProfile); 391 if (mWifiPowerCalculator == null || hasWifiPowerReporting != mHasWifiPowerReporting) { 392 mWifiPowerCalculator = hasWifiPowerReporting ? 393 new WifiPowerCalculator(mPowerProfile) : 394 new WifiPowerEstimator(mPowerProfile); 395 mHasWifiPowerReporting = hasWifiPowerReporting; 396 } 397 mWifiPowerCalculator.reset(); 398 399 final boolean hasBluetoothPowerReporting = checkHasBluetoothPowerReporting(mStats, 400 mPowerProfile); 401 if (mBluetoothPowerCalculator == null || 402 hasBluetoothPowerReporting != mHasBluetoothPowerReporting) { 403 mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile); 404 mHasBluetoothPowerReporting = hasBluetoothPowerReporting; 405 } 406 mBluetoothPowerCalculator.reset(); 407 408 if (mSensorPowerCalculator == null) { 409 mSensorPowerCalculator = new SensorPowerCalculator(mPowerProfile, 410 (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE)); 411 } 412 mSensorPowerCalculator.reset(); 413 414 if (mCameraPowerCalculator == null) { 415 mCameraPowerCalculator = new CameraPowerCalculator(mPowerProfile); 416 } 417 mCameraPowerCalculator.reset(); 418 419 if (mFlashlightPowerCalculator == null) { 420 mFlashlightPowerCalculator = new FlashlightPowerCalculator(mPowerProfile); 421 } 422 mFlashlightPowerCalculator.reset(); 423 424 mStatsType = statsType; 425 mRawUptimeUs = rawUptimeUs; 426 mRawRealtimeUs = rawRealtimeUs; 427 mBatteryUptimeUs = mStats.getBatteryUptime(rawUptimeUs); 428 mBatteryRealtimeUs = mStats.getBatteryRealtime(rawRealtimeUs); 429 mTypeBatteryUptimeUs = mStats.computeBatteryUptime(rawUptimeUs, mStatsType); 430 mTypeBatteryRealtimeUs = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType); 431 mBatteryTimeRemainingUs = mStats.computeBatteryTimeRemaining(rawRealtimeUs); 432 mChargeTimeRemainingUs = mStats.computeChargeTimeRemaining(rawRealtimeUs); 433 434 if (DEBUG) { 435 Log.d(TAG, "Raw time: realtime=" + (rawRealtimeUs / 1000) + " uptime=" 436 + (rawUptimeUs / 1000)); 437 Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtimeUs / 1000) + " uptime=" 438 + (mBatteryUptimeUs / 1000)); 439 Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtimeUs / 1000) + " uptime=" 440 + (mTypeBatteryUptimeUs / 1000)); 441 } 442 mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge() 443 * mPowerProfile.getBatteryCapacity()) / 100; 444 mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge() 445 * mPowerProfile.getBatteryCapacity()) / 100; 446 447 processAppUsage(asUsers); 448 449 // Before aggregating apps in to users, collect all apps to sort by their ms per packet. 450 for (int i = 0; i < mUsageList.size(); i++) { 451 BatterySipper bs = mUsageList.get(i); 452 bs.computeMobilemspp(); 453 if (bs.mobilemspp != 0) { 454 mMobilemsppList.add(bs); 455 } 456 } 457 458 for (int i = 0; i < mUserSippers.size(); i++) { 459 List<BatterySipper> user = mUserSippers.valueAt(i); 460 for (int j = 0; j < user.size(); j++) { 461 BatterySipper bs = user.get(j); 462 bs.computeMobilemspp(); 463 if (bs.mobilemspp != 0) { 464 mMobilemsppList.add(bs); 465 } 466 } 467 } 468 Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() { 469 @Override 470 public int compare(BatterySipper lhs, BatterySipper rhs) { 471 return Double.compare(rhs.mobilemspp, lhs.mobilemspp); 472 } 473 }); 474 475 processMiscUsage(); 476 477 Collections.sort(mUsageList); 478 479 // At this point, we've sorted the list so we are guaranteed the max values are at the top. 480 // We have only added real powers so far. 481 if (!mUsageList.isEmpty()) { 482 mMaxRealPower = mMaxPower = mUsageList.get(0).totalPowerMah; 483 final int usageListCount = mUsageList.size(); 484 for (int i = 0; i < usageListCount; i++) { 485 mComputedPower += mUsageList.get(i).totalPowerMah; 486 } 487 } 488 489 if (DEBUG) { 490 Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge=" 491 + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower)); 492 } 493 494 mTotalPower = mComputedPower; 495 if (mStats.getLowDischargeAmountSinceCharge() > 1) { 496 if (mMinDrainedPower > mComputedPower) { 497 double amount = mMinDrainedPower - mComputedPower; 498 mTotalPower = mMinDrainedPower; 499 BatterySipper bs = new BatterySipper(DrainType.UNACCOUNTED, null, amount); 500 501 // Insert the BatterySipper in its sorted position. 502 int index = Collections.binarySearch(mUsageList, bs); 503 if (index < 0) { 504 index = -(index + 1); 505 } 506 mUsageList.add(index, bs); 507 mMaxPower = Math.max(mMaxPower, amount); 508 } else if (mMaxDrainedPower < mComputedPower) { 509 double amount = mComputedPower - mMaxDrainedPower; 510 511 // Insert the BatterySipper in its sorted position. 512 BatterySipper bs = new BatterySipper(DrainType.OVERCOUNTED, null, amount); 513 int index = Collections.binarySearch(mUsageList, bs); 514 if (index < 0) { 515 index = -(index + 1); 516 } 517 mUsageList.add(index, bs); 518 mMaxPower = Math.max(mMaxPower, amount); 519 } 520 } 521 522 // Smear it! 523 final double hiddenPowerMah = removeHiddenBatterySippers(mUsageList); 524 final double totalRemainingPower = getTotalPower() - hiddenPowerMah; 525 if (Math.abs(totalRemainingPower) > 1e-3) { 526 for (int i = 0, size = mUsageList.size(); i < size; i++) { 527 final BatterySipper sipper = mUsageList.get(i); 528 if (!sipper.shouldHide) { 529 sipper.proportionalSmearMah = hiddenPowerMah 530 * ((sipper.totalPowerMah + sipper.screenPowerMah) 531 / totalRemainingPower); 532 sipper.sumPower(); 533 } 534 } 535 } 536 } 537 processAppUsage(SparseArray<UserHandle> asUsers)538 private void processAppUsage(SparseArray<UserHandle> asUsers) { 539 final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null); 540 mStatsPeriod = mTypeBatteryRealtimeUs; 541 542 BatterySipper osSipper = null; 543 final SparseArray<? extends Uid> uidStats = mStats.getUidStats(); 544 final int NU = uidStats.size(); 545 for (int iu = 0; iu < NU; iu++) { 546 final Uid u = uidStats.valueAt(iu); 547 final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0); 548 549 mCpuPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); 550 mWakelockPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); 551 mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, 552 mStatsType); 553 mWifiPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); 554 mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, 555 mStatsType); 556 mSensorPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); 557 mCameraPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); 558 mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, 559 mStatsType); 560 561 final double totalPower = app.sumPower(); 562 if (DEBUG && totalPower != 0) { 563 Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(), 564 makemAh(totalPower))); 565 } 566 567 // Add the app to the list if it is consuming power. 568 if (totalPower != 0 || u.getUid() == 0) { 569 // 570 // Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list. 571 // 572 final int uid = app.getUid(); 573 final int userId = UserHandle.getUserId(uid); 574 if (uid == Process.WIFI_UID) { 575 mWifiSippers.add(app); 576 } else if (uid == Process.BLUETOOTH_UID) { 577 mBluetoothSippers.add(app); 578 } else if (!forAllUsers && asUsers.get(userId) == null 579 && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) { 580 // We are told to just report this user's apps as one large entry. 581 List<BatterySipper> list = mUserSippers.get(userId); 582 if (list == null) { 583 list = new ArrayList<>(); 584 mUserSippers.put(userId, list); 585 } 586 list.add(app); 587 } else { 588 mUsageList.add(app); 589 } 590 591 if (uid == 0) { 592 osSipper = app; 593 } 594 } 595 } 596 597 if (osSipper != null) { 598 // The device has probably been awake for longer than the screen on 599 // time and application wake lock time would account for. Assign 600 // this remainder to the OS, if possible. 601 mWakelockPowerCalculator.calculateRemaining(osSipper, mStats, mRawRealtimeUs, 602 mRawUptimeUs, mStatsType); 603 osSipper.sumPower(); 604 } 605 } 606 addPhoneUsage()607 private void addPhoneUsage() { 608 long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtimeUs, mStatsType) / 1000; 609 double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) 610 * phoneOnTimeMs / (60 * 60 * 1000); 611 if (phoneOnPower != 0) { 612 addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower); 613 } 614 } 615 616 /** 617 * Screen power is the additional power the screen takes while the device is running. 618 */ addScreenUsage()619 private void addScreenUsage() { 620 double power = 0; 621 long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtimeUs, mStatsType) / 1000; 622 power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON); 623 final double screenFullPower = 624 mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL); 625 for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) { 626 double screenBinPower = screenFullPower * (i + 0.5f) 627 / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; 628 long brightnessTime = mStats.getScreenBrightnessTime(i, mRawRealtimeUs, mStatsType) 629 / 1000; 630 double p = screenBinPower * brightnessTime; 631 if (DEBUG && p != 0) { 632 Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime 633 + " power=" + makemAh(p / (60 * 60 * 1000))); 634 } 635 power += p; 636 } 637 power /= (60 * 60 * 1000); // To hours 638 if (power != 0) { 639 addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power); 640 } 641 } 642 addRadioUsage()643 private void addRadioUsage() { 644 BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0); 645 mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtimeUs, mRawUptimeUs, 646 mStatsType); 647 radio.sumPower(); 648 if (radio.totalPowerMah > 0) { 649 mUsageList.add(radio); 650 } 651 } 652 aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag)653 private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) { 654 for (int i = 0; i < from.size(); i++) { 655 BatterySipper wbs = from.get(i); 656 if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTimeMs); 657 bs.add(wbs); 658 } 659 bs.computeMobilemspp(); 660 bs.sumPower(); 661 } 662 663 /** 664 * Calculate the baseline power usage for the device when it is in suspend and idle. 665 * The device is drawing POWER_CPU_IDLE power at its lowest power state. 666 * The device is drawing POWER_CPU_IDLE + POWER_CPU_AWAKE power when a wakelock is held. 667 */ addIdleUsage()668 private void addIdleUsage() { 669 final double suspendPowerMaMs = (mTypeBatteryRealtimeUs / 1000) * 670 mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE); 671 final double idlePowerMaMs = (mTypeBatteryUptimeUs / 1000) * 672 mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE); 673 final double totalPowerMah = (suspendPowerMaMs + idlePowerMaMs) / (60 * 60 * 1000); 674 if (DEBUG && totalPowerMah != 0) { 675 Log.d(TAG, "Suspend: time=" + (mTypeBatteryRealtimeUs / 1000) 676 + " power=" + makemAh(suspendPowerMaMs / (60 * 60 * 1000))); 677 Log.d(TAG, "Idle: time=" + (mTypeBatteryUptimeUs / 1000) 678 + " power=" + makemAh(idlePowerMaMs / (60 * 60 * 1000))); 679 } 680 681 if (totalPowerMah != 0) { 682 addEntry(BatterySipper.DrainType.IDLE, mTypeBatteryRealtimeUs / 1000, totalPowerMah); 683 } 684 } 685 686 /** 687 * We do per-app blaming of WiFi activity. If energy info is reported from the controller, 688 * then only the WiFi process gets blamed here since we normalize power calculations and 689 * assign all the power drain to apps. If energy info is not reported, we attribute the 690 * difference between total running time of WiFi for all apps and the actual running time 691 * of WiFi to the WiFi subsystem. 692 */ addWiFiUsage()693 private void addWiFiUsage() { 694 BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0); 695 mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtimeUs, mRawUptimeUs, 696 mStatsType); 697 aggregateSippers(bs, mWifiSippers, "WIFI"); 698 if (bs.totalPowerMah > 0) { 699 mUsageList.add(bs); 700 } 701 } 702 703 /** 704 * Bluetooth usage is not attributed to any apps yet, so the entire blame goes to the 705 * Bluetooth Category. 706 */ addBluetoothUsage()707 private void addBluetoothUsage() { 708 BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0); 709 mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtimeUs, mRawUptimeUs, 710 mStatsType); 711 aggregateSippers(bs, mBluetoothSippers, "Bluetooth"); 712 if (bs.totalPowerMah > 0) { 713 mUsageList.add(bs); 714 } 715 } 716 addUserUsage()717 private void addUserUsage() { 718 for (int i = 0; i < mUserSippers.size(); i++) { 719 final int userId = mUserSippers.keyAt(i); 720 BatterySipper bs = new BatterySipper(DrainType.USER, null, 0); 721 bs.userId = userId; 722 aggregateSippers(bs, mUserSippers.valueAt(i), "User"); 723 mUsageList.add(bs); 724 } 725 } 726 addMemoryUsage()727 private void addMemoryUsage() { 728 BatterySipper memory = new BatterySipper(DrainType.MEMORY, null, 0); 729 mMemoryPowerCalculator.calculateRemaining(memory, mStats, mRawRealtimeUs, mRawUptimeUs, 730 mStatsType); 731 memory.sumPower(); 732 if (memory.totalPowerMah > 0) { 733 mUsageList.add(memory); 734 } 735 } 736 processMiscUsage()737 private void processMiscUsage() { 738 addUserUsage(); 739 addPhoneUsage(); 740 addScreenUsage(); 741 addWiFiUsage(); 742 addBluetoothUsage(); 743 addMemoryUsage(); 744 addIdleUsage(); // Not including cellular idle power 745 // Don't compute radio usage if it's a wifi-only device 746 if (!mWifiOnly) { 747 addRadioUsage(); 748 } 749 } 750 addEntry(DrainType drainType, long time, double power)751 private BatterySipper addEntry(DrainType drainType, long time, double power) { 752 BatterySipper bs = new BatterySipper(drainType, null, 0); 753 bs.usagePowerMah = power; 754 bs.usageTimeMs = time; 755 bs.sumPower(); 756 mUsageList.add(bs); 757 return bs; 758 } 759 getUsageList()760 public List<BatterySipper> getUsageList() { 761 return mUsageList; 762 } 763 getMobilemsppList()764 public List<BatterySipper> getMobilemsppList() { 765 return mMobilemsppList; 766 } 767 getStatsPeriod()768 public long getStatsPeriod() { 769 return mStatsPeriod; 770 } 771 getStatsType()772 public int getStatsType() { 773 return mStatsType; 774 } 775 getMaxPower()776 public double getMaxPower() { 777 return mMaxPower; 778 } 779 getMaxRealPower()780 public double getMaxRealPower() { 781 return mMaxRealPower; 782 } 783 getTotalPower()784 public double getTotalPower() { 785 return mTotalPower; 786 } 787 getComputedPower()788 public double getComputedPower() { 789 return mComputedPower; 790 } 791 getMinDrainedPower()792 public double getMinDrainedPower() { 793 return mMinDrainedPower; 794 } 795 getMaxDrainedPower()796 public double getMaxDrainedPower() { 797 return mMaxDrainedPower; 798 } 799 readFully(FileInputStream stream)800 public static byte[] readFully(FileInputStream stream) throws java.io.IOException { 801 return readFully(stream, stream.available()); 802 } 803 readFully(FileInputStream stream, int avail)804 public static byte[] readFully(FileInputStream stream, int avail) throws java.io.IOException { 805 int pos = 0; 806 byte[] data = new byte[avail]; 807 while (true) { 808 int amt = stream.read(data, pos, data.length - pos); 809 //Log.i("foo", "Read " + amt + " bytes at " + pos 810 // + " of avail " + data.length); 811 if (amt <= 0) { 812 //Log.i("foo", "**** FINISHED READING: pos=" + pos 813 // + " len=" + data.length); 814 return data; 815 } 816 pos += amt; 817 avail = stream.available(); 818 if (avail > data.length - pos) { 819 byte[] newData = new byte[pos + avail]; 820 System.arraycopy(data, 0, newData, 0, pos); 821 data = newData; 822 } 823 } 824 } 825 826 /** 827 * Mark the {@link BatterySipper} that we should hide and smear the screen usage based on 828 * foreground activity time. 829 * 830 * @param sippers sipper list that need to check and remove 831 * @return the total power of the hidden items of {@link BatterySipper} 832 * for proportional smearing 833 */ removeHiddenBatterySippers(List<BatterySipper> sippers)834 public double removeHiddenBatterySippers(List<BatterySipper> sippers) { 835 double proportionalSmearPowerMah = 0; 836 BatterySipper screenSipper = null; 837 for (int i = sippers.size() - 1; i >= 0; i--) { 838 final BatterySipper sipper = sippers.get(i); 839 sipper.shouldHide = shouldHideSipper(sipper); 840 if (sipper.shouldHide) { 841 if (sipper.drainType != BatterySipper.DrainType.OVERCOUNTED 842 && sipper.drainType != BatterySipper.DrainType.SCREEN 843 && sipper.drainType != BatterySipper.DrainType.UNACCOUNTED 844 && sipper.drainType != BatterySipper.DrainType.BLUETOOTH 845 && sipper.drainType != BatterySipper.DrainType.WIFI 846 && sipper.drainType != BatterySipper.DrainType.IDLE) { 847 // Don't add it if it is overcounted, unaccounted or screen 848 proportionalSmearPowerMah += sipper.totalPowerMah; 849 } 850 } 851 852 if (sipper.drainType == BatterySipper.DrainType.SCREEN) { 853 screenSipper = sipper; 854 } 855 } 856 857 smearScreenBatterySipper(sippers, screenSipper); 858 859 return proportionalSmearPowerMah; 860 } 861 862 /** 863 * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity 864 * time. 865 */ smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper)866 public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) { 867 long totalActivityTimeMs = 0; 868 final SparseLongArray activityTimeArray = new SparseLongArray(); 869 for (int i = 0, size = sippers.size(); i < size; i++) { 870 final BatteryStats.Uid uid = sippers.get(i).uidObj; 871 if (uid != null) { 872 final long timeMs = getProcessForegroundTimeMs(uid, 873 BatteryStats.STATS_SINCE_CHARGED); 874 activityTimeArray.put(uid.getUid(), timeMs); 875 totalActivityTimeMs += timeMs; 876 } 877 } 878 879 if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) { 880 final double screenPowerMah = screenSipper.totalPowerMah; 881 for (int i = 0, size = sippers.size(); i < size; i++) { 882 final BatterySipper sipper = sippers.get(i); 883 sipper.screenPowerMah = screenPowerMah * activityTimeArray.get(sipper.getUid(), 0) 884 / totalActivityTimeMs; 885 } 886 } 887 } 888 889 /** 890 * Check whether we should hide the battery sipper. 891 */ shouldHideSipper(BatterySipper sipper)892 public boolean shouldHideSipper(BatterySipper sipper) { 893 final BatterySipper.DrainType drainType = sipper.drainType; 894 895 return drainType == BatterySipper.DrainType.IDLE 896 || drainType == BatterySipper.DrainType.CELL 897 || drainType == BatterySipper.DrainType.SCREEN 898 || drainType == BatterySipper.DrainType.UNACCOUNTED 899 || drainType == BatterySipper.DrainType.OVERCOUNTED 900 || isTypeService(sipper) 901 || isTypeSystem(sipper); 902 } 903 904 /** 905 * Check whether {@code sipper} is type service 906 */ isTypeService(BatterySipper sipper)907 public boolean isTypeService(BatterySipper sipper) { 908 final String[] packages = mPackageManager.getPackagesForUid(sipper.getUid()); 909 if (packages == null) { 910 return false; 911 } 912 913 for (String packageName : packages) { 914 if (ArrayUtils.contains(mServicepackageArray, packageName)) { 915 return true; 916 } 917 } 918 919 return false; 920 } 921 922 /** 923 * Check whether {@code sipper} is type system 924 */ isTypeSystem(BatterySipper sipper)925 public boolean isTypeSystem(BatterySipper sipper) { 926 final int uid = sipper.uidObj == null ? -1 : sipper.getUid(); 927 sipper.mPackages = mPackageManager.getPackagesForUid(uid); 928 // Classify all the sippers to type system if the range of uid is 0...FIRST_APPLICATION_UID 929 if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) { 930 return true; 931 } else if (sipper.mPackages != null) { 932 for (final String packageName : sipper.mPackages) { 933 if (ArrayUtils.contains(mSystemPackageArray, packageName)) { 934 return true; 935 } 936 } 937 } 938 939 return false; 940 } 941 convertUsToMs(long timeUs)942 public long convertUsToMs(long timeUs) { 943 return timeUs / 1000; 944 } 945 convertMsToUs(long timeMs)946 public long convertMsToUs(long timeMs) { 947 return timeMs * 1000; 948 } 949 950 @VisibleForTesting getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)951 public long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) { 952 final BatteryStats.Timer timer = uid.getForegroundActivityTimer(); 953 if (timer != null) { 954 return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); 955 } 956 957 return 0; 958 } 959 960 @VisibleForTesting getProcessForegroundTimeMs(BatteryStats.Uid uid, int which)961 public long getProcessForegroundTimeMs(BatteryStats.Uid uid, int which) { 962 final long rawRealTimeUs = convertMsToUs(SystemClock.elapsedRealtime()); 963 final int foregroundTypes[] = {BatteryStats.Uid.PROCESS_STATE_TOP}; 964 965 long timeUs = 0; 966 for (int type : foregroundTypes) { 967 final long localTime = uid.getProcessStateTime(type, rawRealTimeUs, which); 968 timeUs += localTime; 969 } 970 971 // Return the min value of STATE_TOP time and foreground activity time, since both of these 972 // time have some errors. 973 return convertUsToMs( 974 Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs))); 975 } 976 977 @VisibleForTesting setPackageManager(PackageManager packageManager)978 public void setPackageManager(PackageManager packageManager) { 979 mPackageManager = packageManager; 980 } 981 982 @VisibleForTesting setSystemPackageArray(String[] array)983 public void setSystemPackageArray(String[] array) { 984 mSystemPackageArray = array; 985 } 986 987 @VisibleForTesting setServicePackageArray(String[] array)988 public void setServicePackageArray(String[] array) { 989 mServicepackageArray = array; 990 } 991 load()992 private void load() { 993 if (mBatteryInfo == null) { 994 return; 995 } 996 mStats = getStats(mBatteryInfo); 997 if (mCollectBatteryBroadcast) { 998 mBatteryBroadcast = mContext.registerReceiver(null, 999 new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 1000 } 1001 } 1002 getStats(IBatteryStats service)1003 private static BatteryStatsImpl getStats(IBatteryStats service) { 1004 try { 1005 ParcelFileDescriptor pfd = service.getStatisticsStream(); 1006 if (pfd != null) { 1007 try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) { 1008 byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor())); 1009 Parcel parcel = Parcel.obtain(); 1010 parcel.unmarshall(data, 0, data.length); 1011 parcel.setDataPosition(0); 1012 BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR 1013 .createFromParcel(parcel); 1014 return stats; 1015 } catch (IOException e) { 1016 Log.w(TAG, "Unable to read statistics stream", e); 1017 } 1018 } 1019 } catch (RemoteException e) { 1020 Log.w(TAG, "RemoteException:", e); 1021 } 1022 return new BatteryStatsImpl(); 1023 } 1024 } 1025