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