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