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 static android.os.BatteryStats.NETWORK_MOBILE_RX_DATA; 20 import static android.os.BatteryStats.NETWORK_MOBILE_TX_DATA; 21 import static android.os.BatteryStats.NETWORK_WIFI_RX_DATA; 22 import static android.os.BatteryStats.NETWORK_WIFI_TX_DATA; 23 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.hardware.Sensor; 28 import android.hardware.SensorManager; 29 import android.net.ConnectivityManager; 30 import android.os.BatteryStats; 31 import android.os.BatteryStats.Uid; 32 import android.os.Bundle; 33 import android.os.MemoryFile; 34 import android.os.Parcel; 35 import android.os.ParcelFileDescriptor; 36 import android.os.Process; 37 import android.os.RemoteException; 38 import android.os.ServiceManager; 39 import android.os.SystemClock; 40 import android.os.UserHandle; 41 import android.telephony.SignalStrength; 42 import android.util.ArrayMap; 43 import android.util.Log; 44 import android.util.SparseArray; 45 46 import com.android.internal.app.IBatteryStats; 47 import com.android.internal.os.BatterySipper.DrainType; 48 49 import java.io.File; 50 import java.io.FileInputStream; 51 import java.io.FileOutputStream; 52 import java.io.IOException; 53 import java.util.ArrayList; 54 import java.util.Collections; 55 import java.util.Comparator; 56 import java.util.List; 57 import java.util.Map; 58 59 /** 60 * A helper class for retrieving the power usage information for all applications and services. 61 * 62 * The caller must initialize this class as soon as activity object is ready to use (for example, in 63 * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy(). 64 */ 65 public final class BatteryStatsHelper { 66 67 private 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 private IBatteryStats mBatteryInfo; 80 private BatteryStats mStats; 81 private Intent mBatteryBroadcast; 82 private PowerProfile mPowerProfile; 83 84 private final List<BatterySipper> mUsageList = new ArrayList<BatterySipper>(); 85 private final List<BatterySipper> mWifiSippers = new ArrayList<BatterySipper>(); 86 private final List<BatterySipper> mBluetoothSippers = new ArrayList<BatterySipper>(); 87 private final SparseArray<List<BatterySipper>> mUserSippers 88 = new SparseArray<List<BatterySipper>>(); 89 private final SparseArray<Double> mUserPower = new SparseArray<Double>(); 90 91 private final List<BatterySipper> mMobilemsppList = new ArrayList<BatterySipper>(); 92 93 private int mStatsType = BatteryStats.STATS_SINCE_CHARGED; 94 95 long mRawRealtime; 96 long mRawUptime; 97 long mBatteryRealtime; 98 long mBatteryUptime; 99 long mTypeBatteryRealtime; 100 long mTypeBatteryUptime; 101 long mBatteryTimeRemaining; 102 long mChargeTimeRemaining; 103 104 private long mStatsPeriod = 0; 105 private double mMaxPower = 1; 106 private double mMaxRealPower = 1; 107 private double mComputedPower; 108 private double mTotalPower; 109 private double mWifiPower; 110 private double mBluetoothPower; 111 private double mMinDrainedPower; 112 private double mMaxDrainedPower; 113 114 // How much the apps together have kept the mobile radio active. 115 private long mAppMobileActive; 116 117 // How much the apps together have left WIFI running. 118 private long mAppWifiRunning; 119 BatteryStatsHelper(Context context)120 public BatteryStatsHelper(Context context) { 121 this(context, true); 122 } 123 BatteryStatsHelper(Context context, boolean collectBatteryBroadcast)124 public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) { 125 mContext = context; 126 mCollectBatteryBroadcast = collectBatteryBroadcast; 127 mWifiOnly = checkWifiOnly(context); 128 } 129 BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly)130 public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) { 131 mContext = context; 132 mCollectBatteryBroadcast = collectBatteryBroadcast; 133 mWifiOnly = wifiOnly; 134 } 135 checkWifiOnly(Context context)136 public static boolean checkWifiOnly(Context context) { 137 ConnectivityManager cm = (ConnectivityManager)context.getSystemService( 138 Context.CONNECTIVITY_SERVICE); 139 return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); 140 } 141 storeStatsHistoryInFile(String fname)142 public void storeStatsHistoryInFile(String fname) { 143 synchronized (sFileXfer) { 144 File path = makeFilePath(mContext, fname); 145 sFileXfer.put(path, this.getStats()); 146 FileOutputStream fout = null; 147 try { 148 fout = new FileOutputStream(path); 149 Parcel hist = Parcel.obtain(); 150 getStats().writeToParcelWithoutUids(hist, 0); 151 byte[] histData = hist.marshall(); 152 fout.write(histData); 153 } catch (IOException e) { 154 Log.w(TAG, "Unable to write history to file", e); 155 } finally { 156 if (fout != null) { 157 try { 158 fout.close(); 159 } catch (IOException e) { 160 } 161 } 162 } 163 } 164 } 165 statsFromFile(Context context, String fname)166 public static BatteryStats statsFromFile(Context context, String fname) { 167 synchronized (sFileXfer) { 168 File path = makeFilePath(context, fname); 169 BatteryStats stats = sFileXfer.get(path); 170 if (stats != null) { 171 return stats; 172 } 173 FileInputStream fin = null; 174 try { 175 fin = new FileInputStream(path); 176 byte[] data = readFully(fin); 177 Parcel parcel = Parcel.obtain(); 178 parcel.unmarshall(data, 0, data.length); 179 parcel.setDataPosition(0); 180 return com.android.internal.os.BatteryStatsImpl.CREATOR.createFromParcel(parcel); 181 } catch (IOException e) { 182 Log.w(TAG, "Unable to read history to file", e); 183 } finally { 184 if (fin != null) { 185 try { 186 fin.close(); 187 } catch (IOException e) { 188 } 189 } 190 } 191 } 192 return getStats(IBatteryStats.Stub.asInterface( 193 ServiceManager.getService(BatteryStats.SERVICE_NAME))); 194 } 195 dropFile(Context context, String fname)196 public static void dropFile(Context context, String fname) { 197 makeFilePath(context, fname).delete(); 198 } 199 makeFilePath(Context context, String fname)200 private static File makeFilePath(Context context, String fname) { 201 return new File(context.getFilesDir(), fname); 202 } 203 204 /** Clears the current stats and forces recreating for future use. */ clearStats()205 public void clearStats() { 206 mStats = null; 207 } 208 getStats()209 public BatteryStats getStats() { 210 if (mStats == null) { 211 load(); 212 } 213 return mStats; 214 } 215 getBatteryBroadcast()216 public Intent getBatteryBroadcast() { 217 if (mBatteryBroadcast == null && mCollectBatteryBroadcast) { 218 load(); 219 } 220 return mBatteryBroadcast; 221 } 222 getPowerProfile()223 public PowerProfile getPowerProfile() { 224 return mPowerProfile; 225 } 226 create(BatteryStats stats)227 public void create(BatteryStats stats) { 228 mPowerProfile = new PowerProfile(mContext); 229 mStats = stats; 230 } 231 create(Bundle icicle)232 public void create(Bundle icicle) { 233 if (icicle != null) { 234 mStats = sStatsXfer; 235 mBatteryBroadcast = sBatteryBroadcastXfer; 236 } 237 mBatteryInfo = IBatteryStats.Stub.asInterface( 238 ServiceManager.getService(BatteryStats.SERVICE_NAME)); 239 mPowerProfile = new PowerProfile(mContext); 240 } 241 storeState()242 public void storeState() { 243 sStatsXfer = mStats; 244 sBatteryBroadcastXfer = mBatteryBroadcast; 245 } 246 makemAh(double power)247 public static String makemAh(double power) { 248 if (power < .00001) return String.format("%.8f", power); 249 else if (power < .0001) return String.format("%.7f", power); 250 else if (power < .001) return String.format("%.6f", power); 251 else if (power < .01) return String.format("%.5f", power); 252 else if (power < .1) return String.format("%.4f", power); 253 else if (power < 1) return String.format("%.3f", power); 254 else if (power < 10) return String.format("%.2f", power); 255 else if (power < 100) return String.format("%.1f", power); 256 else return String.format("%.0f", power); 257 } 258 259 /** 260 * Refreshes the power usage list. 261 */ refreshStats(int statsType, int asUser)262 public void refreshStats(int statsType, int asUser) { 263 SparseArray<UserHandle> users = new SparseArray<UserHandle>(1); 264 users.put(asUser, new UserHandle(asUser)); 265 refreshStats(statsType, users); 266 } 267 268 /** 269 * Refreshes the power usage list. 270 */ refreshStats(int statsType, List<UserHandle> asUsers)271 public void refreshStats(int statsType, List<UserHandle> asUsers) { 272 final int n = asUsers.size(); 273 SparseArray<UserHandle> users = new SparseArray<UserHandle>(n); 274 for (int i = 0; i < n; ++i) { 275 UserHandle userHandle = asUsers.get(i); 276 users.put(userHandle.getIdentifier(), userHandle); 277 } 278 refreshStats(statsType, users); 279 } 280 281 /** 282 * Refreshes the power usage list. 283 */ refreshStats(int statsType, SparseArray<UserHandle> asUsers)284 public void refreshStats(int statsType, SparseArray<UserHandle> asUsers) { 285 refreshStats(statsType, asUsers, SystemClock.elapsedRealtime() * 1000, 286 SystemClock.uptimeMillis() * 1000); 287 } 288 refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs, long rawUptimeUs)289 public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs, 290 long rawUptimeUs) { 291 // Initialize mStats if necessary. 292 getStats(); 293 294 mMaxPower = 0; 295 mMaxRealPower = 0; 296 mComputedPower = 0; 297 mTotalPower = 0; 298 mWifiPower = 0; 299 mBluetoothPower = 0; 300 mAppMobileActive = 0; 301 mAppWifiRunning = 0; 302 303 mUsageList.clear(); 304 mWifiSippers.clear(); 305 mBluetoothSippers.clear(); 306 mUserSippers.clear(); 307 mUserPower.clear(); 308 mMobilemsppList.clear(); 309 310 if (mStats == null) { 311 return; 312 } 313 314 mStatsType = statsType; 315 mRawUptime = rawUptimeUs; 316 mRawRealtime = rawRealtimeUs; 317 mBatteryUptime = mStats.getBatteryUptime(rawUptimeUs); 318 mBatteryRealtime = mStats.getBatteryRealtime(rawRealtimeUs); 319 mTypeBatteryUptime = mStats.computeBatteryUptime(rawUptimeUs, mStatsType); 320 mTypeBatteryRealtime = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType); 321 mBatteryTimeRemaining = mStats.computeBatteryTimeRemaining(rawRealtimeUs); 322 mChargeTimeRemaining = mStats.computeChargeTimeRemaining(rawRealtimeUs); 323 324 if (DEBUG) { 325 Log.d(TAG, "Raw time: realtime=" + (rawRealtimeUs/1000) + " uptime=" 326 + (rawUptimeUs/1000)); 327 Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtime/1000) + " uptime=" 328 + (mBatteryUptime/1000)); 329 Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtime/1000) + " uptime=" 330 + (mTypeBatteryUptime/1000)); 331 } 332 mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge() 333 * mPowerProfile.getBatteryCapacity()) / 100; 334 mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge() 335 * mPowerProfile.getBatteryCapacity()) / 100; 336 337 processAppUsage(asUsers); 338 339 // Before aggregating apps in to users, collect all apps to sort by their ms per packet. 340 for (int i=0; i<mUsageList.size(); i++) { 341 BatterySipper bs = mUsageList.get(i); 342 bs.computeMobilemspp(); 343 if (bs.mobilemspp != 0) { 344 mMobilemsppList.add(bs); 345 } 346 } 347 for (int i=0; i<mUserSippers.size(); i++) { 348 List<BatterySipper> user = mUserSippers.valueAt(i); 349 for (int j=0; j<user.size(); j++) { 350 BatterySipper bs = user.get(j); 351 bs.computeMobilemspp(); 352 if (bs.mobilemspp != 0) { 353 mMobilemsppList.add(bs); 354 } 355 } 356 } 357 Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() { 358 @Override 359 public int compare(BatterySipper lhs, BatterySipper rhs) { 360 if (lhs.mobilemspp < rhs.mobilemspp) { 361 return 1; 362 } else if (lhs.mobilemspp > rhs.mobilemspp) { 363 return -1; 364 } 365 return 0; 366 } 367 }); 368 369 processMiscUsage(); 370 371 if (DEBUG) { 372 Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge=" 373 + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower)); 374 } 375 mTotalPower = mComputedPower; 376 if (mStats.getLowDischargeAmountSinceCharge() > 1) { 377 if (mMinDrainedPower > mComputedPower) { 378 double amount = mMinDrainedPower - mComputedPower; 379 mTotalPower = mMinDrainedPower; 380 addEntryNoTotal(BatterySipper.DrainType.UNACCOUNTED, 0, amount); 381 } else if (mMaxDrainedPower < mComputedPower) { 382 double amount = mComputedPower - mMaxDrainedPower; 383 addEntryNoTotal(BatterySipper.DrainType.OVERCOUNTED, 0, amount); 384 } 385 } 386 387 Collections.sort(mUsageList); 388 } 389 processAppUsage(SparseArray<UserHandle> asUsers)390 private void processAppUsage(SparseArray<UserHandle> asUsers) { 391 final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null); 392 SensorManager sensorManager = (SensorManager) mContext.getSystemService( 393 Context.SENSOR_SERVICE); 394 final int which = mStatsType; 395 final int speedSteps = mPowerProfile.getNumSpeedSteps(); 396 final double[] powerCpuNormal = new double[speedSteps]; 397 final long[] cpuSpeedStepTimes = new long[speedSteps]; 398 for (int p = 0; p < speedSteps; p++) { 399 powerCpuNormal[p] = mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p); 400 } 401 final double mobilePowerPerPacket = getMobilePowerPerPacket(); 402 final double mobilePowerPerMs = getMobilePowerPerMs(); 403 final double wifiPowerPerPacket = getWifiPowerPerPacket(); 404 long appWakelockTimeUs = 0; 405 BatterySipper osApp = null; 406 mStatsPeriod = mTypeBatteryRealtime; 407 SparseArray<? extends Uid> uidStats = mStats.getUidStats(); 408 final int NU = uidStats.size(); 409 for (int iu = 0; iu < NU; iu++) { 410 Uid u = uidStats.valueAt(iu); 411 double p; // in mAs 412 double power = 0; // in mAs 413 double highestDrain = 0; 414 String packageWithHighestDrain = null; 415 Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); 416 long cpuTime = 0; 417 long cpuFgTime = 0; 418 long wakelockTime = 0; 419 long gpsTime = 0; 420 if (processStats.size() > 0) { 421 // Process CPU time 422 for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent 423 : processStats.entrySet()) { 424 Uid.Proc ps = ent.getValue(); 425 final long userTime = ps.getUserTime(which); 426 final long systemTime = ps.getSystemTime(which); 427 final long foregroundTime = ps.getForegroundTime(which); 428 cpuFgTime += foregroundTime * 10; // convert to millis 429 final long tmpCpuTime = (userTime + systemTime) * 10; // convert to millis 430 int totalTimeAtSpeeds = 0; 431 // Get the total first 432 for (int step = 0; step < speedSteps; step++) { 433 cpuSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, which); 434 totalTimeAtSpeeds += cpuSpeedStepTimes[step]; 435 } 436 if (totalTimeAtSpeeds == 0) totalTimeAtSpeeds = 1; 437 // Then compute the ratio of time spent at each speed 438 double processPower = 0; 439 for (int step = 0; step < speedSteps; step++) { 440 double ratio = (double) cpuSpeedStepTimes[step] / totalTimeAtSpeeds; 441 if (DEBUG && ratio != 0) Log.d(TAG, "UID " + u.getUid() + ": CPU step #" 442 + step + " ratio=" + makemAh(ratio) + " power=" 443 + makemAh(ratio*tmpCpuTime*powerCpuNormal[step] / (60*60*1000))); 444 processPower += ratio * tmpCpuTime * powerCpuNormal[step]; 445 } 446 cpuTime += tmpCpuTime; 447 if (DEBUG && processPower != 0) { 448 Log.d(TAG, String.format("process %s, cpu power=%s", 449 ent.getKey(), makemAh(processPower / (60*60*1000)))); 450 } 451 power += processPower; 452 if (packageWithHighestDrain == null 453 || packageWithHighestDrain.startsWith("*")) { 454 highestDrain = processPower; 455 packageWithHighestDrain = ent.getKey(); 456 } else if (highestDrain < processPower 457 && !ent.getKey().startsWith("*")) { 458 highestDrain = processPower; 459 packageWithHighestDrain = ent.getKey(); 460 } 461 } 462 } 463 if (cpuFgTime > cpuTime) { 464 if (DEBUG && cpuFgTime > cpuTime + 10000) { 465 Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time"); 466 } 467 cpuTime = cpuFgTime; // Statistics may not have been gathered yet. 468 } 469 power /= (60*60*1000); 470 471 // Process wake lock usage 472 Map<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats = u.getWakelockStats(); 473 for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> wakelockEntry 474 : wakelockStats.entrySet()) { 475 Uid.Wakelock wakelock = wakelockEntry.getValue(); 476 // Only care about partial wake locks since full wake locks 477 // are canceled when the user turns the screen off. 478 BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL); 479 if (timer != null) { 480 wakelockTime += timer.getTotalTimeLocked(mRawRealtime, which); 481 } 482 } 483 appWakelockTimeUs += wakelockTime; 484 wakelockTime /= 1000; // convert to millis 485 486 // Add cost of holding a wake lock 487 p = (wakelockTime 488 * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / (60*60*1000); 489 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wake " 490 + wakelockTime + " power=" + makemAh(p)); 491 power += p; 492 493 // Add cost of mobile traffic 494 final long mobileRx = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType); 495 final long mobileTx = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType); 496 final long mobileRxB = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, mStatsType); 497 final long mobileTxB = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, mStatsType); 498 final long mobileActive = u.getMobileRadioActiveTime(mStatsType); 499 if (mobileActive > 0) { 500 // We are tracking when the radio is up, so can use the active time to 501 // determine power use. 502 mAppMobileActive += mobileActive; 503 p = (mobilePowerPerMs * mobileActive) / 1000; 504 } else { 505 // We are not tracking when the radio is up, so must approximate power use 506 // based on the number of packets. 507 p = (mobileRx + mobileTx) * mobilePowerPerPacket; 508 } 509 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": mobile packets " 510 + (mobileRx+mobileTx) + " active time " + mobileActive 511 + " power=" + makemAh(p)); 512 power += p; 513 514 // Add cost of wifi traffic 515 final long wifiRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, mStatsType); 516 final long wifiTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, mStatsType); 517 final long wifiRxB = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, mStatsType); 518 final long wifiTxB = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, mStatsType); 519 p = (wifiRx + wifiTx) * wifiPowerPerPacket; 520 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi packets " 521 + (mobileRx+mobileTx) + " power=" + makemAh(p)); 522 power += p; 523 524 // Add cost of keeping WIFI running. 525 long wifiRunningTimeMs = u.getWifiRunningTime(mRawRealtime, which) / 1000; 526 mAppWifiRunning += wifiRunningTimeMs; 527 p = (wifiRunningTimeMs 528 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / (60*60*1000); 529 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi running " 530 + wifiRunningTimeMs + " power=" + makemAh(p)); 531 power += p; 532 533 // Add cost of WIFI scans 534 long wifiScanTimeMs = u.getWifiScanTime(mRawRealtime, which) / 1000; 535 p = (wifiScanTimeMs 536 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_SCAN)) / (60*60*1000); 537 if (DEBUG) Log.d(TAG, "UID " + u.getUid() + ": wifi scan " + wifiScanTimeMs 538 + " power=" + makemAh(p)); 539 power += p; 540 for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) { 541 long batchScanTimeMs = u.getWifiBatchedScanTime(bin, mRawRealtime, which) / 1000; 542 p = ((batchScanTimeMs 543 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, bin)) 544 ) / (60*60*1000); 545 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi batched scan # " + bin 546 + " time=" + batchScanTimeMs + " power=" + makemAh(p)); 547 power += p; 548 } 549 550 // Process Sensor usage 551 SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats(); 552 int NSE = sensorStats.size(); 553 for (int ise=0; ise<NSE; ise++) { 554 Uid.Sensor sensor = sensorStats.valueAt(ise); 555 int sensorHandle = sensorStats.keyAt(ise); 556 BatteryStats.Timer timer = sensor.getSensorTime(); 557 long sensorTime = timer.getTotalTimeLocked(mRawRealtime, which) / 1000; 558 double multiplier = 0; 559 switch (sensorHandle) { 560 case Uid.Sensor.GPS: 561 multiplier = mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_ON); 562 gpsTime = sensorTime; 563 break; 564 default: 565 List<Sensor> sensorList = sensorManager.getSensorList( 566 android.hardware.Sensor.TYPE_ALL); 567 for (android.hardware.Sensor s : sensorList) { 568 if (s.getHandle() == sensorHandle) { 569 multiplier = s.getPower(); 570 break; 571 } 572 } 573 } 574 p = (multiplier * sensorTime) / (60*60*1000); 575 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": sensor #" + sensorHandle 576 + " time=" + sensorTime + " power=" + makemAh(p)); 577 power += p; 578 } 579 580 if (DEBUG && power != 0) Log.d(TAG, String.format("UID %d: total power=%s", 581 u.getUid(), makemAh(power))); 582 583 // Add the app to the list if it is consuming power 584 final int userId = UserHandle.getUserId(u.getUid()); 585 if (power != 0 || u.getUid() == 0) { 586 BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 587 new double[] {power}); 588 app.cpuTime = cpuTime; 589 app.gpsTime = gpsTime; 590 app.wifiRunningTime = wifiRunningTimeMs; 591 app.cpuFgTime = cpuFgTime; 592 app.wakeLockTime = wakelockTime; 593 app.mobileRxPackets = mobileRx; 594 app.mobileTxPackets = mobileTx; 595 app.mobileActive = mobileActive / 1000; 596 app.mobileActiveCount = u.getMobileRadioActiveCount(mStatsType); 597 app.wifiRxPackets = wifiRx; 598 app.wifiTxPackets = wifiTx; 599 app.mobileRxBytes = mobileRxB; 600 app.mobileTxBytes = mobileTxB; 601 app.wifiRxBytes = wifiRxB; 602 app.wifiTxBytes = wifiTxB; 603 app.packageWithHighestDrain = packageWithHighestDrain; 604 if (u.getUid() == Process.WIFI_UID) { 605 mWifiSippers.add(app); 606 mWifiPower += power; 607 } else if (u.getUid() == Process.BLUETOOTH_UID) { 608 mBluetoothSippers.add(app); 609 mBluetoothPower += power; 610 } else if (!forAllUsers && asUsers.get(userId) == null 611 && UserHandle.getAppId(u.getUid()) >= Process.FIRST_APPLICATION_UID) { 612 List<BatterySipper> list = mUserSippers.get(userId); 613 if (list == null) { 614 list = new ArrayList<BatterySipper>(); 615 mUserSippers.put(userId, list); 616 } 617 list.add(app); 618 if (power != 0) { 619 Double userPower = mUserPower.get(userId); 620 if (userPower == null) { 621 userPower = power; 622 } else { 623 userPower += power; 624 } 625 mUserPower.put(userId, userPower); 626 } 627 } else { 628 mUsageList.add(app); 629 if (power > mMaxPower) mMaxPower = power; 630 if (power > mMaxRealPower) mMaxRealPower = power; 631 mComputedPower += power; 632 } 633 if (u.getUid() == 0) { 634 osApp = app; 635 } 636 } 637 } 638 639 // The device has probably been awake for longer than the screen on 640 // time and application wake lock time would account for. Assign 641 // this remainder to the OS, if possible. 642 if (osApp != null) { 643 long wakeTimeMillis = mBatteryUptime / 1000; 644 wakeTimeMillis -= (appWakelockTimeUs / 1000) 645 + (mStats.getScreenOnTime(mRawRealtime, which) / 1000); 646 if (wakeTimeMillis > 0) { 647 double power = (wakeTimeMillis 648 * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) 649 / (60*60*1000); 650 if (DEBUG) Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power " 651 + makemAh(power)); 652 osApp.wakeLockTime += wakeTimeMillis; 653 osApp.value += power; 654 osApp.values[0] += power; 655 if (osApp.value > mMaxPower) mMaxPower = osApp.value; 656 if (osApp.value > mMaxRealPower) mMaxRealPower = osApp.value; 657 mComputedPower += power; 658 } 659 } 660 } 661 addPhoneUsage()662 private void addPhoneUsage() { 663 long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtime, mStatsType) / 1000; 664 double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) 665 * phoneOnTimeMs / (60*60*1000); 666 if (phoneOnPower != 0) { 667 BatterySipper bs = addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower); 668 } 669 } 670 addScreenUsage()671 private void addScreenUsage() { 672 double power = 0; 673 long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtime, mStatsType) / 1000; 674 power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON); 675 final double screenFullPower = 676 mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL); 677 for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) { 678 double screenBinPower = screenFullPower * (i + 0.5f) 679 / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; 680 long brightnessTime = mStats.getScreenBrightnessTime(i, mRawRealtime, mStatsType) 681 / 1000; 682 double p = screenBinPower*brightnessTime; 683 if (DEBUG && p != 0) { 684 Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime 685 + " power=" + makemAh(p / (60 * 60 * 1000))); 686 } 687 power += p; 688 } 689 power /= (60*60*1000); // To hours 690 if (power != 0) { 691 addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power); 692 } 693 } 694 addRadioUsage()695 private void addRadioUsage() { 696 double power = 0; 697 final int BINS = SignalStrength.NUM_SIGNAL_STRENGTH_BINS; 698 long signalTimeMs = 0; 699 long noCoverageTimeMs = 0; 700 for (int i = 0; i < BINS; i++) { 701 long strengthTimeMs = mStats.getPhoneSignalStrengthTime(i, mRawRealtime, mStatsType) 702 / 1000; 703 double p = (strengthTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ON, i)) 704 / (60*60*1000); 705 if (DEBUG && p != 0) { 706 Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power=" 707 + makemAh(p)); 708 } 709 power += p; 710 signalTimeMs += strengthTimeMs; 711 if (i == 0) { 712 noCoverageTimeMs = strengthTimeMs; 713 } 714 } 715 long scanningTimeMs = mStats.getPhoneSignalScanningTime(mRawRealtime, mStatsType) 716 / 1000; 717 double p = (scanningTimeMs * mPowerProfile.getAveragePower( 718 PowerProfile.POWER_RADIO_SCANNING)) 719 / (60*60*1000); 720 if (DEBUG && p != 0) { 721 Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs + " power=" + makemAh(p)); 722 } 723 power += p; 724 long radioActiveTimeUs = mStats.getMobileRadioActiveTime(mRawRealtime, mStatsType); 725 long remainingActiveTime = (radioActiveTimeUs - mAppMobileActive) / 1000; 726 if (remainingActiveTime > 0) { 727 power += getMobilePowerPerMs() * remainingActiveTime; 728 } 729 if (power != 0) { 730 BatterySipper bs = 731 addEntry(BatterySipper.DrainType.CELL, signalTimeMs, power); 732 if (signalTimeMs != 0) { 733 bs.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs; 734 } 735 bs.mobileActive = remainingActiveTime; 736 bs.mobileActiveCount = mStats.getMobileRadioActiveUnknownCount(mStatsType); 737 } 738 } 739 aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag)740 private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) { 741 for (int i=0; i<from.size(); i++) { 742 BatterySipper wbs = from.get(i); 743 if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTime); 744 bs.cpuTime += wbs.cpuTime; 745 bs.gpsTime += wbs.gpsTime; 746 bs.wifiRunningTime += wbs.wifiRunningTime; 747 bs.cpuFgTime += wbs.cpuFgTime; 748 bs.wakeLockTime += wbs.wakeLockTime; 749 bs.mobileRxPackets += wbs.mobileRxPackets; 750 bs.mobileTxPackets += wbs.mobileTxPackets; 751 bs.mobileActive += wbs.mobileActive; 752 bs.mobileActiveCount += wbs.mobileActiveCount; 753 bs.wifiRxPackets += wbs.wifiRxPackets; 754 bs.wifiTxPackets += wbs.wifiTxPackets; 755 bs.mobileRxBytes += wbs.mobileRxBytes; 756 bs.mobileTxBytes += wbs.mobileTxBytes; 757 bs.wifiRxBytes += wbs.wifiRxBytes; 758 bs.wifiTxBytes += wbs.wifiTxBytes; 759 } 760 bs.computeMobilemspp(); 761 } 762 addWiFiUsage()763 private void addWiFiUsage() { 764 long onTimeMs = mStats.getWifiOnTime(mRawRealtime, mStatsType) / 1000; 765 long runningTimeMs = mStats.getGlobalWifiRunningTime(mRawRealtime, mStatsType) / 1000; 766 if (DEBUG) Log.d(TAG, "WIFI runningTime=" + runningTimeMs 767 + " app runningTime=" + mAppWifiRunning); 768 runningTimeMs -= mAppWifiRunning; 769 if (runningTimeMs < 0) runningTimeMs = 0; 770 double wifiPower = (onTimeMs * 0 /* TODO */ 771 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON) 772 + runningTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) 773 / (60*60*1000); 774 if (DEBUG && wifiPower != 0) { 775 Log.d(TAG, "Wifi: time=" + runningTimeMs + " power=" + makemAh(wifiPower)); 776 } 777 if ((wifiPower+mWifiPower) != 0) { 778 BatterySipper bs = addEntry(BatterySipper.DrainType.WIFI, runningTimeMs, 779 wifiPower + mWifiPower); 780 aggregateSippers(bs, mWifiSippers, "WIFI"); 781 } 782 } 783 addIdleUsage()784 private void addIdleUsage() { 785 long idleTimeMs = (mTypeBatteryRealtime 786 - mStats.getScreenOnTime(mRawRealtime, mStatsType)) / 1000; 787 double idlePower = (idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE)) 788 / (60*60*1000); 789 if (DEBUG && idlePower != 0) { 790 Log.d(TAG, "Idle: time=" + idleTimeMs + " power=" + makemAh(idlePower)); 791 } 792 if (idlePower != 0) { 793 addEntry(BatterySipper.DrainType.IDLE, idleTimeMs, idlePower); 794 } 795 } 796 addBluetoothUsage()797 private void addBluetoothUsage() { 798 long btOnTimeMs = mStats.getBluetoothOnTime(mRawRealtime, mStatsType) / 1000; 799 double btPower = btOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_ON) 800 / (60*60*1000); 801 if (DEBUG && btPower != 0) { 802 Log.d(TAG, "Bluetooth: time=" + btOnTimeMs + " power=" + makemAh(btPower)); 803 } 804 int btPingCount = mStats.getBluetoothPingCount(); 805 double pingPower = (btPingCount 806 * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_AT_CMD)) 807 / (60*60*1000); 808 if (DEBUG && pingPower != 0) { 809 Log.d(TAG, "Bluetooth ping: count=" + btPingCount + " power=" + makemAh(pingPower)); 810 } 811 btPower += pingPower; 812 if ((btPower+mBluetoothPower) != 0) { 813 BatterySipper bs = addEntry(BatterySipper.DrainType.BLUETOOTH, btOnTimeMs, 814 btPower + mBluetoothPower); 815 aggregateSippers(bs, mBluetoothSippers, "Bluetooth"); 816 } 817 } 818 addFlashlightUsage()819 private void addFlashlightUsage() { 820 long flashlightOnTimeMs = mStats.getFlashlightOnTime(mRawRealtime, mStatsType) / 1000; 821 double flashlightPower = flashlightOnTimeMs 822 * mPowerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT) / (60*60*1000); 823 if (flashlightPower != 0) { 824 addEntry(BatterySipper.DrainType.FLASHLIGHT, flashlightOnTimeMs, flashlightPower); 825 } 826 } 827 addUserUsage()828 private void addUserUsage() { 829 for (int i=0; i<mUserSippers.size(); i++) { 830 final int userId = mUserSippers.keyAt(i); 831 final List<BatterySipper> sippers = mUserSippers.valueAt(i); 832 Double userPower = mUserPower.get(userId); 833 double power = (userPower != null) ? userPower : 0.0; 834 BatterySipper bs = addEntry(BatterySipper.DrainType.USER, 0, power); 835 bs.userId = userId; 836 aggregateSippers(bs, sippers, "User"); 837 } 838 } 839 840 /** 841 * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio. 842 */ getMobilePowerPerPacket()843 private double getMobilePowerPerPacket() { 844 final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system 845 final double MOBILE_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) 846 / 3600; 847 848 final long mobileRx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType); 849 final long mobileTx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType); 850 final long mobileData = mobileRx + mobileTx; 851 852 final long radioDataUptimeMs 853 = mStats.getMobileRadioActiveTime(mRawRealtime, mStatsType) / 1000; 854 final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0) 855 ? (mobileData / (double)radioDataUptimeMs) 856 : (((double)MOBILE_BPS) / 8 / 2048); 857 858 return (MOBILE_POWER / mobilePps) / (60*60); 859 } 860 861 /** 862 * Return estimated power (in mAs) of keeping the radio up 863 */ getMobilePowerPerMs()864 private double getMobilePowerPerMs() { 865 return mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) / (60*60*1000); 866 } 867 868 /** 869 * Return estimated power (in mAs) of sending a byte with the Wi-Fi radio. 870 */ getWifiPowerPerPacket()871 private double getWifiPowerPerPacket() { 872 final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system 873 final double WIFI_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE) 874 / 3600; 875 return (WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048)) / (60*60); 876 } 877 processMiscUsage()878 private void processMiscUsage() { 879 addUserUsage(); 880 addPhoneUsage(); 881 addScreenUsage(); 882 addFlashlightUsage(); 883 addWiFiUsage(); 884 addBluetoothUsage(); 885 addIdleUsage(); // Not including cellular idle power 886 // Don't compute radio usage if it's a wifi-only device 887 if (!mWifiOnly) { 888 addRadioUsage(); 889 } 890 } 891 addEntry(DrainType drainType, long time, double power)892 private BatterySipper addEntry(DrainType drainType, long time, double power) { 893 mComputedPower += power; 894 if (power > mMaxRealPower) mMaxRealPower = power; 895 return addEntryNoTotal(drainType, time, power); 896 } 897 addEntryNoTotal(DrainType drainType, long time, double power)898 private BatterySipper addEntryNoTotal(DrainType drainType, long time, double power) { 899 if (power > mMaxPower) mMaxPower = power; 900 BatterySipper bs = new BatterySipper(drainType, null, new double[] {power}); 901 bs.usageTime = time; 902 mUsageList.add(bs); 903 return bs; 904 } 905 getUsageList()906 public List<BatterySipper> getUsageList() { 907 return mUsageList; 908 } 909 getMobilemsppList()910 public List<BatterySipper> getMobilemsppList() { 911 return mMobilemsppList; 912 } 913 getStatsPeriod()914 public long getStatsPeriod() { return mStatsPeriod; } 915 getStatsType()916 public int getStatsType() { return mStatsType; }; 917 getMaxPower()918 public double getMaxPower() { return mMaxPower; } 919 getMaxRealPower()920 public double getMaxRealPower() { return mMaxRealPower; } 921 getTotalPower()922 public double getTotalPower() { return mTotalPower; } 923 getComputedPower()924 public double getComputedPower() { return mComputedPower; } 925 getMinDrainedPower()926 public double getMinDrainedPower() { 927 return mMinDrainedPower; 928 } 929 getMaxDrainedPower()930 public double getMaxDrainedPower() { 931 return mMaxDrainedPower; 932 } 933 getBatteryTimeRemaining()934 public long getBatteryTimeRemaining() { return mBatteryTimeRemaining; } 935 getChargeTimeRemaining()936 public long getChargeTimeRemaining() { return mChargeTimeRemaining; } 937 readFully(FileInputStream stream)938 public static byte[] readFully(FileInputStream stream) throws java.io.IOException { 939 return readFully(stream, stream.available()); 940 } 941 readFully(FileInputStream stream, int avail)942 public static byte[] readFully(FileInputStream stream, int avail) throws java.io.IOException { 943 int pos = 0; 944 byte[] data = new byte[avail]; 945 while (true) { 946 int amt = stream.read(data, pos, data.length-pos); 947 //Log.i("foo", "Read " + amt + " bytes at " + pos 948 // + " of avail " + data.length); 949 if (amt <= 0) { 950 //Log.i("foo", "**** FINISHED READING: pos=" + pos 951 // + " len=" + data.length); 952 return data; 953 } 954 pos += amt; 955 avail = stream.available(); 956 if (avail > data.length-pos) { 957 byte[] newData = new byte[pos+avail]; 958 System.arraycopy(data, 0, newData, 0, pos); 959 data = newData; 960 } 961 } 962 } 963 load()964 private void load() { 965 if (mBatteryInfo == null) { 966 return; 967 } 968 mStats = getStats(mBatteryInfo); 969 if (mCollectBatteryBroadcast) { 970 mBatteryBroadcast = mContext.registerReceiver(null, 971 new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 972 } 973 } 974 getStats(IBatteryStats service)975 private static BatteryStatsImpl getStats(IBatteryStats service) { 976 try { 977 ParcelFileDescriptor pfd = service.getStatisticsStream(); 978 if (pfd != null) { 979 FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd); 980 try { 981 byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor())); 982 Parcel parcel = Parcel.obtain(); 983 parcel.unmarshall(data, 0, data.length); 984 parcel.setDataPosition(0); 985 BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR 986 .createFromParcel(parcel); 987 stats.distributeWorkLocked(BatteryStats.STATS_SINCE_CHARGED); 988 return stats; 989 } catch (IOException e) { 990 Log.w(TAG, "Unable to read statistics stream", e); 991 } 992 } 993 } catch (RemoteException e) { 994 Log.w(TAG, "RemoteException:", e); 995 } 996 return new BatteryStatsImpl(); 997 } 998 } 999