Adding per UID WiFi power distribution.
Change-Id: Ia3d97e0a1c3352127185c18626d8ba8221c9ab40
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 508fdee..3051926 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -269,6 +269,15 @@
public abstract long getTotalTimeLocked(long elapsedRealtimeUs, int which);
/**
+ * Returns the total time in microseconds associated with this Timer since the
+ * 'mark' was last set.
+ *
+ * @param elapsedRealtimeUs current elapsed realtime of system in microseconds
+ * @return a time in microseconds
+ */
+ public abstract long getTimeSinceMarkLocked(long elapsedRealtimeUs);
+
+ /**
* Temporary for debugging.
*/
public abstract void logState(Printer pw, String prefix);
@@ -332,7 +341,17 @@
* @return a Map from Strings to Uid.Pkg objects.
*/
public abstract ArrayMap<String, ? extends Pkg> getPackageStats();
-
+
+ /**
+ * Returns the time in milliseconds that this app kept the WiFi controller in the
+ * specified state <code>type</code>.
+ * @param type one of {@link #CONTROLLER_IDLE_TIME}, {@link #CONTROLLER_RX_TIME}, or
+ * {@link #CONTROLLER_TX_TIME}.
+ * @param which one of {@link #STATS_CURRENT}, {@link #STATS_SINCE_CHARGED}, or
+ * {@link #STATS_SINCE_UNPLUGGED}.
+ */
+ public abstract long getWifiControllerActivity(int type, int which);
+
/**
* {@hide}
*/
@@ -1914,7 +1933,6 @@
public static final int NETWORK_MOBILE_TX_DATA = 1;
public static final int NETWORK_WIFI_RX_DATA = 2;
public static final int NETWORK_WIFI_TX_DATA = 3;
-
public static final int NUM_NETWORK_ACTIVITY_TYPES = NETWORK_WIFI_TX_DATA + 1;
public abstract long getNetworkActivityBytes(int type, int which);
@@ -1923,10 +1941,25 @@
public static final int CONTROLLER_IDLE_TIME = 0;
public static final int CONTROLLER_RX_TIME = 1;
public static final int CONTROLLER_TX_TIME = 2;
- public static final int CONTROLLER_ENERGY = 3;
- public static final int NUM_CONTROLLER_ACTIVITY_TYPES = CONTROLLER_ENERGY + 1;
+ public static final int CONTROLLER_POWER_DRAIN = 3;
+ public static final int NUM_CONTROLLER_ACTIVITY_TYPES = CONTROLLER_POWER_DRAIN + 1;
+ /**
+ * For {@link #CONTROLLER_IDLE_TIME}, {@link #CONTROLLER_RX_TIME}, and
+ * {@link #CONTROLLER_TX_TIME}, returns the time spent (in milliseconds) in the
+ * respective state.
+ * For {@link #CONTROLLER_POWER_DRAIN}, returns the power used by the controller in
+ * milli-ampere-milliseconds (mAms).
+ */
public abstract long getBluetoothControllerActivity(int type, int which);
+
+ /**
+ * For {@link #CONTROLLER_IDLE_TIME}, {@link #CONTROLLER_RX_TIME}, and
+ * {@link #CONTROLLER_TX_TIME}, returns the time spent (in milliseconds) in the
+ * respective state.
+ * For {@link #CONTROLLER_POWER_DRAIN}, returns the power used by the controller in
+ * milli-ampere-milliseconds (mAms).
+ */
public abstract long getWifiControllerActivity(int type, int which);
/**
@@ -2618,7 +2651,7 @@
label = "???";
}
dumpLine(pw, uid, category, POWER_USE_ITEM_DATA, label,
- BatteryStatsHelper.makemAh(bs.value));
+ BatteryStatsHelper.makemAh(bs.totalPowerMah));
}
}
@@ -3264,6 +3297,13 @@
sb.setLength(0);
sb.append(prefix);
+ sb.append(" WiFi Energy use: ").append(BatteryStatsHelper.makemAh(
+ getWifiControllerActivity(CONTROLLER_POWER_DRAIN, which) / (double)(1000*60*60)));
+ sb.append(" mAh");
+ pw.println(sb.toString());
+
+ sb.setLength(0);
+ sb.append(prefix);
sb.append(" Bluetooth on: "); formatTimeMs(sb, bluetoothOnTime / 1000);
sb.append("("); sb.append(formatRatioLocked(bluetoothOnTime, whichBatteryRealtime));
sb.append(")");
@@ -3376,48 +3416,48 @@
final BatterySipper bs = sippers.get(i);
switch (bs.drainType) {
case IDLE:
- pw.print(prefix); pw.print(" Idle: "); printmAh(pw, bs.value);
+ pw.print(prefix); pw.print(" Idle: "); printmAh(pw, bs.totalPowerMah);
pw.println();
break;
case CELL:
- pw.print(prefix); pw.print(" Cell standby: "); printmAh(pw, bs.value);
+ pw.print(prefix); pw.print(" Cell standby: "); printmAh(pw, bs.totalPowerMah);
pw.println();
break;
case PHONE:
- pw.print(prefix); pw.print(" Phone calls: "); printmAh(pw, bs.value);
+ pw.print(prefix); pw.print(" Phone calls: "); printmAh(pw, bs.totalPowerMah);
pw.println();
break;
case WIFI:
- pw.print(prefix); pw.print(" Wifi: "); printmAh(pw, bs.value);
+ pw.print(prefix); pw.print(" Wifi: "); printmAh(pw, bs.totalPowerMah);
pw.println();
break;
case BLUETOOTH:
- pw.print(prefix); pw.print(" Bluetooth: "); printmAh(pw, bs.value);
+ pw.print(prefix); pw.print(" Bluetooth: "); printmAh(pw, bs.totalPowerMah);
pw.println();
break;
case SCREEN:
- pw.print(prefix); pw.print(" Screen: "); printmAh(pw, bs.value);
+ pw.print(prefix); pw.print(" Screen: "); printmAh(pw, bs.totalPowerMah);
pw.println();
break;
case FLASHLIGHT:
- pw.print(prefix); pw.print(" Flashlight: "); printmAh(pw, bs.value);
+ pw.print(prefix); pw.print(" Flashlight: "); printmAh(pw, bs.totalPowerMah);
pw.println();
break;
case APP:
pw.print(prefix); pw.print(" Uid ");
UserHandle.formatUid(pw, bs.uidObj.getUid());
- pw.print(": "); printmAh(pw, bs.value); pw.println();
+ pw.print(": "); printmAh(pw, bs.totalPowerMah); pw.println();
break;
case USER:
pw.print(prefix); pw.print(" User "); pw.print(bs.userId);
- pw.print(": "); printmAh(pw, bs.value); pw.println();
+ pw.print(": "); printmAh(pw, bs.totalPowerMah); pw.println();
break;
case UNACCOUNTED:
- pw.print(prefix); pw.print(" Unaccounted: "); printmAh(pw, bs.value);
+ pw.print(prefix); pw.print(" Unaccounted: "); printmAh(pw, bs.totalPowerMah);
pw.println();
break;
case OVERCOUNTED:
- pw.print(prefix); pw.print(" Over-counted: "); printmAh(pw, bs.value);
+ pw.print(prefix); pw.print(" Over-counted: "); printmAh(pw, bs.totalPowerMah);
pw.println();
break;
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index bea4ece..1746bed 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -109,6 +109,7 @@
void noteWifiBatchedScanStoppedFromSource(in WorkSource ws);
void noteWifiMulticastEnabledFromSource(in WorkSource ws);
void noteWifiMulticastDisabledFromSource(in WorkSource ws);
+ void noteWifiRadioPowerState(int powerState, long timestampNs);
void noteNetworkInterfaceType(String iface, int type);
void noteNetworkStatsEnabled();
void noteDeviceIdleMode(boolean enabled, boolean fromActive, boolean fromMotion);
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index 4cd959f..056b0aa 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -23,17 +23,25 @@
public class BatterySipper implements Comparable<BatterySipper> {
public int userId;
public Uid uidObj;
- public double value;
- public double[] values;
+ public double totalPowerMah;
public DrainType drainType;
- // Measured in milliseconds.
- public long usageTime;
- public long cpuTime;
- public long gpsTime;
- public long wifiRunningTime;
- public long cpuFgTime;
- public long wakeLockTime;
+ /**
+ * Generic usage time in milliseconds.
+ */
+ public long usageTimeMs;
+
+ /**
+ * Generic power usage in mAh.
+ */
+ public double usagePowerMah;
+
+ // Subsystem usage times.
+ public long cpuTimeMs;
+ public long gpsTimeMs;
+ public long wifiRunningTimeMs;
+ public long cpuFgTimeMs;
+ public long wakeLockTimeMs;
public long mobileRxPackets;
public long mobileTxPackets;
@@ -52,12 +60,13 @@
public String packageWithHighestDrain;
// Measured in mAh (milli-ampere per hour).
- public double wifiPower;
- public double cpuPower;
- public double wakeLockPower;
- public double mobileRadioPower;
- public double gpsPower;
- public double sensorPower;
+ // These are included when summed.
+ public double wifiPowerMah;
+ public double cpuPowerMah;
+ public double wakeLockPowerMah;
+ public double mobileRadioPowerMah;
+ public double gpsPowerMah;
+ public double sensorPowerMah;
public enum DrainType {
IDLE,
@@ -73,17 +82,12 @@
OVERCOUNTED
}
- public BatterySipper(DrainType drainType, Uid uid, double[] values) {
- this.values = values;
- if (values != null) value = values[0];
+ public BatterySipper(DrainType drainType, Uid uid, double value) {
+ this.totalPowerMah = value;
this.drainType = drainType;
uidObj = uid;
}
- public double[] getValues() {
- return values;
- }
-
public void computeMobilemspp() {
long packets = mobileRxPackets+mobileTxPackets;
mobilemspp = packets > 0 ? (mobileActive / (double)packets) : 0;
@@ -101,7 +105,7 @@
}
}
// Return the flipped value because we want the items in descending order
- return Double.compare(other.value, value);
+ return Double.compare(other.totalPowerMah, totalPowerMah);
}
/**
@@ -123,11 +127,14 @@
* Add stats from other to this BatterySipper.
*/
public void add(BatterySipper other) {
- cpuTime += other.cpuTime;
- gpsTime += other.gpsTime;
- wifiRunningTime += other.wifiRunningTime;
- cpuFgTime += other.cpuFgTime;
- wakeLockTime += other.wakeLockTime;
+ totalPowerMah += other.totalPowerMah;
+ usageTimeMs += other.usageTimeMs;
+ usagePowerMah += other.usagePowerMah;
+ cpuTimeMs += other.cpuTimeMs;
+ gpsTimeMs += other.gpsTimeMs;
+ wifiRunningTimeMs += other.wifiRunningTimeMs;
+ cpuFgTimeMs += other.cpuFgTimeMs;
+ wakeLockTimeMs += other.wakeLockTimeMs;
mobileRxPackets += other.mobileRxPackets;
mobileTxPackets += other.mobileTxPackets;
mobileActive += other.mobileActive;
@@ -138,11 +145,20 @@
mobileTxBytes += other.mobileTxBytes;
wifiRxBytes += other.wifiRxBytes;
wifiTxBytes += other.wifiTxBytes;
- wifiPower += other.wifiPower;
- gpsPower += other.gpsPower;
- cpuPower += other.cpuPower;
- sensorPower += other.sensorPower;
- mobileRadioPower += other.mobileRadioPower;
- wakeLockPower += other.wakeLockPower;
+ wifiPowerMah += other.wifiPowerMah;
+ gpsPowerMah += other.gpsPowerMah;
+ cpuPowerMah += other.cpuPowerMah;
+ sensorPowerMah += other.sensorPowerMah;
+ mobileRadioPowerMah += other.mobileRadioPowerMah;
+ wakeLockPowerMah += other.wakeLockPowerMah;
+ }
+
+ /**
+ * Sum all the powers and store the value into `value`.
+ * @return the sum of all the power in this BatterySipper.
+ */
+ public double sumPower() {
+ return totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah + sensorPowerMah
+ + mobileRadioPowerMah + wakeLockPowerMah;
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index d3611bf..024b7c5 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -16,17 +16,12 @@
package com.android.internal.os;
-import static android.os.BatteryStats.NETWORK_MOBILE_RX_DATA;
-import static android.os.BatteryStats.NETWORK_MOBILE_TX_DATA;
-import static android.os.BatteryStats.NETWORK_WIFI_RX_DATA;
-import static android.os.BatteryStats.NETWORK_WIFI_TX_DATA;
-
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
import android.os.BatteryStats;
import android.os.BatteryStats.Uid;
import android.os.Bundle;
@@ -38,7 +33,6 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.telephony.SignalStrength;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
@@ -54,7 +48,6 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-import java.util.Map;
/**
* A helper class for retrieving the power usage information for all applications and services.
@@ -63,8 +56,7 @@
* onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy().
*/
public final class BatteryStatsHelper {
-
- private static final boolean DEBUG = false;
+ static final boolean DEBUG = false;
private static final String TAG = BatteryStatsHelper.class.getSimpleName();
@@ -81,14 +73,24 @@
private Intent mBatteryBroadcast;
private PowerProfile mPowerProfile;
- private final List<BatterySipper> mUsageList = new ArrayList<BatterySipper>();
- private final List<BatterySipper> mWifiSippers = new ArrayList<BatterySipper>();
- private final List<BatterySipper> mBluetoothSippers = new ArrayList<BatterySipper>();
- private final SparseArray<List<BatterySipper>> mUserSippers
- = new SparseArray<List<BatterySipper>>();
- private final SparseArray<Double> mUserPower = new SparseArray<Double>();
+ /**
+ * List of apps using power.
+ */
+ private final List<BatterySipper> mUsageList = new ArrayList<>();
- private final List<BatterySipper> mMobilemsppList = new ArrayList<BatterySipper>();
+ /**
+ * List of apps using wifi power.
+ */
+ private final List<BatterySipper> mWifiSippers = new ArrayList<>();
+
+ /**
+ * List of apps using bluetooth power.
+ */
+ private final List<BatterySipper> mBluetoothSippers = new ArrayList<>();
+
+ private final SparseArray<List<BatterySipper>> mUserSippers = new SparseArray<>();
+
+ private final List<BatterySipper> mMobilemsppList = new ArrayList<>();
private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
@@ -102,29 +104,50 @@
long mChargeTimeRemaining;
private long mStatsPeriod = 0;
+
+ // The largest entry by power.
private double mMaxPower = 1;
+
+ // The largest real entry by power (not undercounted or overcounted).
private double mMaxRealPower = 1;
+
+ // Total computed power.
private double mComputedPower;
private double mTotalPower;
- private double mWifiPower;
- private double mBluetoothPower;
private double mMinDrainedPower;
private double mMaxDrainedPower;
- // How much the apps together have kept the mobile radio active.
- private long mAppMobileActive;
+ PowerCalculator mCpuPowerCalculator;
+ PowerCalculator mWakelockPowerCalculator;
+ MobileRadioPowerCalculator mMobileRadioPowerCalculator;
+ PowerCalculator mWifiPowerCalculator;
+ PowerCalculator mBluetoothPowerCalculator;
+ PowerCalculator mSensorPowerCalculator;
- // How much the apps together have left WIFI running.
- private long mAppWifiRunning;
+ public static boolean checkWifiOnly(Context context) {
+ ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ }
+
+ public static boolean checkHasWifiPowerReporting(Context context, PowerProfile profile) {
+ WifiManager manager = context.getSystemService(WifiManager.class);
+ if (manager.isEnhancedPowerReportingSupported()) {
+ if (profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE) != 0 &&
+ profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX) != 0 &&
+ profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
public BatteryStatsHelper(Context context) {
this(context, true);
}
public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) {
- mContext = context;
- mCollectBatteryBroadcast = collectBatteryBroadcast;
- mWifiOnly = checkWifiOnly(context);
+ this(context, collectBatteryBroadcast, checkWifiOnly(context));
}
public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) {
@@ -133,12 +156,6 @@
mWifiOnly = wifiOnly;
}
- public static boolean checkWifiOnly(Context context) {
- ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
- }
-
public void storeStatsHistoryInFile(String fname) {
synchronized (sFileXfer) {
File path = makeFilePath(mContext, fname);
@@ -260,7 +277,7 @@
* Refreshes the power usage list.
*/
public void refreshStats(int statsType, int asUser) {
- SparseArray<UserHandle> users = new SparseArray<UserHandle>(1);
+ SparseArray<UserHandle> users = new SparseArray<>(1);
users.put(asUser, new UserHandle(asUser));
refreshStats(statsType, users);
}
@@ -270,7 +287,7 @@
*/
public void refreshStats(int statsType, List<UserHandle> asUsers) {
final int n = asUsers.size();
- SparseArray<UserHandle> users = new SparseArray<UserHandle>(n);
+ SparseArray<UserHandle> users = new SparseArray<>(n);
for (int i = 0; i < n; ++i) {
UserHandle userHandle = asUsers.get(i);
users.put(userHandle.getIdentifier(), userHandle);
@@ -295,22 +312,52 @@
mMaxRealPower = 0;
mComputedPower = 0;
mTotalPower = 0;
- mWifiPower = 0;
- mBluetoothPower = 0;
- mAppMobileActive = 0;
- mAppWifiRunning = 0;
mUsageList.clear();
mWifiSippers.clear();
mBluetoothSippers.clear();
mUserSippers.clear();
- mUserPower.clear();
mMobilemsppList.clear();
if (mStats == null) {
return;
}
+ if (mCpuPowerCalculator == null) {
+ mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
+ }
+ mCpuPowerCalculator.reset();
+
+ if (mWakelockPowerCalculator == null) {
+ mWakelockPowerCalculator = new WakelockPowerCalculator(mPowerProfile);
+ }
+ mWakelockPowerCalculator.reset();
+
+ if (mMobileRadioPowerCalculator == null) {
+ mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile, mStats);
+ }
+ mMobileRadioPowerCalculator.reset(mStats);
+
+ if (mWifiPowerCalculator == null) {
+ if (checkHasWifiPowerReporting(mContext, mPowerProfile)) {
+ mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
+ } else {
+ mWifiPowerCalculator = new WifiPowerEstimator(mPowerProfile);
+ }
+ }
+ mWifiPowerCalculator.reset();
+
+ if (mBluetoothPowerCalculator == null) {
+ mBluetoothPowerCalculator = new BluetoothPowerCalculator();
+ }
+ mBluetoothPowerCalculator.reset();
+
+ if (mSensorPowerCalculator == null) {
+ mSensorPowerCalculator = new SensorPowerCalculator(mPowerProfile,
+ (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE));
+ }
+ mSensorPowerCalculator.reset();
+
mStatsType = statsType;
mRawUptime = rawUptimeUs;
mRawRealtime = rawRealtimeUs;
@@ -358,383 +405,113 @@
Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() {
@Override
public int compare(BatterySipper lhs, BatterySipper rhs) {
- if (lhs.mobilemspp < rhs.mobilemspp) {
- return 1;
- } else if (lhs.mobilemspp > rhs.mobilemspp) {
- return -1;
- }
- return 0;
+ return Double.compare(rhs.mobilemspp, lhs.mobilemspp);
}
});
processMiscUsage();
+ Collections.sort(mUsageList);
+
+ // At this point, we've sorted the list so we are guaranteed the max values are at the top.
+ // We have only added real powers so far.
+ if (!mUsageList.isEmpty()) {
+ mMaxRealPower = mMaxPower = mUsageList.get(0).totalPowerMah;
+ final int usageListCount = mUsageList.size();
+ for (int i = 0; i < usageListCount; i++) {
+ mComputedPower += mUsageList.get(i).totalPowerMah;
+ }
+ }
+
if (DEBUG) {
Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge="
+ makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower));
}
+
mTotalPower = mComputedPower;
if (mStats.getLowDischargeAmountSinceCharge() > 1) {
if (mMinDrainedPower > mComputedPower) {
double amount = mMinDrainedPower - mComputedPower;
mTotalPower = mMinDrainedPower;
- addEntryNoTotal(BatterySipper.DrainType.UNACCOUNTED, 0, amount);
+ BatterySipper bs = new BatterySipper(DrainType.UNACCOUNTED, null, amount);
+
+ // Insert the BatterySipper in its sorted position.
+ int index = Collections.binarySearch(mUsageList, bs);
+ if (index < 0) {
+ index = -(index + 1);
+ }
+ mUsageList.add(index, bs);
+ mMaxPower = Math.max(mMaxPower, amount);
} else if (mMaxDrainedPower < mComputedPower) {
double amount = mComputedPower - mMaxDrainedPower;
- addEntryNoTotal(BatterySipper.DrainType.OVERCOUNTED, 0, amount);
+
+ // Insert the BatterySipper in its sorted position.
+ BatterySipper bs = new BatterySipper(DrainType.OVERCOUNTED, null, amount);
+ int index = Collections.binarySearch(mUsageList, bs);
+ if (index < 0) {
+ index = -(index + 1);
+ }
+ mUsageList.add(index, bs);
+ mMaxPower = Math.max(mMaxPower, amount);
}
}
-
- Collections.sort(mUsageList);
}
private void processAppUsage(SparseArray<UserHandle> asUsers) {
final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
- final SensorManager sensorManager =
- (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
- final int which = mStatsType;
- final int speedSteps = mPowerProfile.getNumSpeedSteps();
- final double[] powerCpuNormal = new double[speedSteps];
- final long[] cpuSpeedStepTimes = new long[speedSteps];
- for (int p = 0; p < speedSteps; p++) {
- powerCpuNormal[p] = mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p);
- }
- final double mobilePowerPerPacket = getMobilePowerPerPacket();
- final double mobilePowerPerMs = getMobilePowerPerMs();
- final double wifiPowerPerPacket = getWifiPowerPerPacket();
- long totalAppWakelockTimeUs = 0;
- BatterySipper osApp = null;
mStatsPeriod = mTypeBatteryRealtime;
- final ArrayList<BatterySipper> appList = new ArrayList<>();
-
- // Max values used to normalize later.
- double maxWifiPower = 0;
- double maxCpuPower = 0;
- double maxWakeLockPower = 0;
- double maxMobileRadioPower = 0;
- double maxGpsPower = 0;
- double maxSensorPower = 0;
-
final SparseArray<? extends Uid> uidStats = mStats.getUidStats();
final int NU = uidStats.size();
for (int iu = 0; iu < NU; iu++) {
final Uid u = uidStats.valueAt(iu);
- final BatterySipper app = new BatterySipper(
- BatterySipper.DrainType.APP, u, new double[]{0});
+ final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);
- final Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
- if (processStats.size() > 0) {
- // Process CPU time.
+ mCpuPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
+ mWakelockPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
+ mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
+ mWifiPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
+ mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
+ mSensorPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
- // Keep track of the package with highest drain.
- double highestDrain = 0;
-
- for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
- : processStats.entrySet()) {
- Uid.Proc ps = ent.getValue();
- app.cpuFgTime += ps.getForegroundTime(which);
- final long totalCpuTime = ps.getUserTime(which) + ps.getSystemTime(which);
- app.cpuTime += totalCpuTime;
-
- // Calculate the total CPU time spent at the various speed steps.
- long totalTimeAtSpeeds = 0;
- for (int step = 0; step < speedSteps; step++) {
- cpuSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, which);
- totalTimeAtSpeeds += cpuSpeedStepTimes[step];
- }
- totalTimeAtSpeeds = Math.max(totalTimeAtSpeeds, 1);
-
- // Then compute the ratio of time spent at each speed and figure out
- // the total power consumption.
- double cpuPower = 0;
- for (int step = 0; step < speedSteps; step++) {
- final double ratio = (double) cpuSpeedStepTimes[step] / totalTimeAtSpeeds;
- final double cpuSpeedStepPower =
- ratio * totalCpuTime * powerCpuNormal[step];
- if (DEBUG && ratio != 0) {
- Log.d(TAG, "UID " + u.getUid() + ": CPU step #"
- + step + " ratio=" + makemAh(ratio) + " power="
- + makemAh(cpuSpeedStepPower / (60 * 60 * 1000)));
- }
- cpuPower += cpuSpeedStepPower;
- }
-
- if (DEBUG && cpuPower != 0) {
- Log.d(TAG, String.format("process %s, cpu power=%s",
- ent.getKey(), makemAh(cpuPower / (60 * 60 * 1000))));
- }
- app.cpuPower += cpuPower;
-
- // Each App can have multiple packages and with multiple running processes.
- // Keep track of the package who's process has the highest drain.
- if (app.packageWithHighestDrain == null ||
- app.packageWithHighestDrain.startsWith("*")) {
- highestDrain = cpuPower;
- app.packageWithHighestDrain = ent.getKey();
- } else if (highestDrain < cpuPower && !ent.getKey().startsWith("*")) {
- highestDrain = cpuPower;
- app.packageWithHighestDrain = ent.getKey();
- }
- }
- }
-
- // Ensure that the CPU times make sense.
- if (app.cpuFgTime > app.cpuTime) {
- if (DEBUG && app.cpuFgTime > app.cpuTime + 10000) {
- Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
- }
-
- // Statistics may not have been gathered yet.
- app.cpuTime = app.cpuFgTime;
- }
-
- // Convert the CPU power to mAh
- app.cpuPower /= (60 * 60 * 1000);
- maxCpuPower = Math.max(maxCpuPower, app.cpuPower);
-
- // Process wake lock usage
- final Map<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
- u.getWakelockStats();
- long wakeLockTimeUs = 0;
- for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> wakelockEntry
- : wakelockStats.entrySet()) {
- final Uid.Wakelock wakelock = wakelockEntry.getValue();
-
- // Only care about partial wake locks since full wake locks
- // are canceled when the user turns the screen off.
- BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
- if (timer != null) {
- wakeLockTimeUs += timer.getTotalTimeLocked(mRawRealtime, which);
- }
- }
- app.wakeLockTime = wakeLockTimeUs / 1000; // convert to millis
- totalAppWakelockTimeUs += wakeLockTimeUs;
-
- // Add cost of holding a wake lock.
- app.wakeLockPower = (app.wakeLockTime *
- mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / (60 * 60 * 1000);
- if (DEBUG && app.wakeLockPower != 0) {
- Log.d(TAG, "UID " + u.getUid() + ": wake "
- + app.wakeLockTime + " power=" + makemAh(app.wakeLockPower));
- }
- maxWakeLockPower = Math.max(maxWakeLockPower, app.wakeLockPower);
-
- // Add cost of mobile traffic.
- final long mobileActive = u.getMobileRadioActiveTime(mStatsType);
- app.mobileRxPackets = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType);
- app.mobileTxPackets = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType);
- app.mobileActive = mobileActive / 1000;
- app.mobileActiveCount = u.getMobileRadioActiveCount(mStatsType);
- app.mobileRxBytes = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, mStatsType);
- app.mobileTxBytes = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, mStatsType);
-
- if (mobileActive > 0) {
- // We are tracking when the radio is up, so can use the active time to
- // determine power use.
- mAppMobileActive += mobileActive;
- app.mobileRadioPower = (mobilePowerPerMs * mobileActive) / 1000;
- } else {
- // We are not tracking when the radio is up, so must approximate power use
- // based on the number of packets.
- app.mobileRadioPower = (app.mobileRxPackets + app.mobileTxPackets)
- * mobilePowerPerPacket;
- }
- if (DEBUG && app.mobileRadioPower != 0) {
- Log.d(TAG, "UID " + u.getUid() + ": mobile packets "
- + (app.mobileRxPackets + app.mobileTxPackets)
- + " active time " + mobileActive
- + " power=" + makemAh(app.mobileRadioPower));
- }
- maxMobileRadioPower = Math.max(maxMobileRadioPower, app.mobileRadioPower);
-
- // Add cost of wifi traffic
- app.wifiRxPackets = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, mStatsType);
- app.wifiTxPackets = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, mStatsType);
- app.wifiRxBytes = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, mStatsType);
- app.wifiTxBytes = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, mStatsType);
-
- final double wifiPacketPower = (app.wifiRxPackets + app.wifiTxPackets)
- * wifiPowerPerPacket;
- if (DEBUG && wifiPacketPower != 0) {
- Log.d(TAG, "UID " + u.getUid() + ": wifi packets "
- + (app.wifiRxPackets + app.wifiTxPackets)
- + " power=" + makemAh(wifiPacketPower));
- }
-
- // Add cost of keeping WIFI running.
- app.wifiRunningTime = u.getWifiRunningTime(mRawRealtime, which) / 1000;
- mAppWifiRunning += app.wifiRunningTime;
-
- final double wifiLockPower = (app.wifiRunningTime
- * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / (60 * 60 * 1000);
- if (DEBUG && wifiLockPower != 0) {
- Log.d(TAG, "UID " + u.getUid() + ": wifi running "
- + app.wifiRunningTime + " power=" + makemAh(wifiLockPower));
- }
-
- // Add cost of WIFI scans
- final long wifiScanTimeMs = u.getWifiScanTime(mRawRealtime, which) / 1000;
- final double wifiScanPower = (wifiScanTimeMs
- * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_SCAN))
- / (60 * 60 * 1000);
- if (DEBUG && wifiScanPower != 0) {
- Log.d(TAG, "UID " + u.getUid() + ": wifi scan " + wifiScanTimeMs
- + " power=" + makemAh(wifiScanPower));
- }
-
- // Add cost of WIFI batch scans.
- double wifiBatchScanPower = 0;
- for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
- final long batchScanTimeMs =
- u.getWifiBatchedScanTime(bin, mRawRealtime, which) / 1000;
- final double batchScanPower = ((batchScanTimeMs
- * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, bin))
- ) / (60 * 60 * 1000);
- if (DEBUG && batchScanPower != 0) {
- Log.d(TAG, "UID " + u.getUid() + ": wifi batched scan # " + bin
- + " time=" + batchScanTimeMs + " power=" + makemAh(batchScanPower));
- }
- wifiBatchScanPower += batchScanPower;
- }
-
- // Add up all the WiFi costs.
- app.wifiPower = wifiPacketPower + wifiLockPower + wifiScanPower + wifiBatchScanPower;
- maxWifiPower = Math.max(maxWifiPower, app.wifiPower);
-
- // Process Sensor usage
- final SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
- final int NSE = sensorStats.size();
- for (int ise = 0; ise < NSE; ise++) {
- final Uid.Sensor sensor = sensorStats.valueAt(ise);
- final int sensorHandle = sensorStats.keyAt(ise);
- final BatteryStats.Timer timer = sensor.getSensorTime();
- final long sensorTime = timer.getTotalTimeLocked(mRawRealtime, which) / 1000;
- double sensorPower = 0;
- switch (sensorHandle) {
- case Uid.Sensor.GPS:
- app.gpsTime = sensorTime;
- app.gpsPower = (app.gpsTime
- * mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_ON))
- / (60 * 60 * 1000);
- sensorPower = app.gpsPower;
- maxGpsPower = Math.max(maxGpsPower, app.gpsPower);
- break;
- default:
- List<Sensor> sensorList = sensorManager.getSensorList(
- android.hardware.Sensor.TYPE_ALL);
- for (android.hardware.Sensor s : sensorList) {
- if (s.getHandle() == sensorHandle) {
- sensorPower = (sensorTime * s.getPower()) / (60 * 60 * 1000);
- app.sensorPower += sensorPower;
- break;
- }
- }
- }
- if (DEBUG && sensorPower != 0) {
- Log.d(TAG, "UID " + u.getUid() + ": sensor #" + sensorHandle
- + " time=" + sensorTime + " power=" + makemAh(sensorPower));
- }
- }
- maxSensorPower = Math.max(maxSensorPower, app.sensorPower);
-
- final double totalUnnormalizedPower = app.cpuPower + app.wifiPower + app.wakeLockPower
- + app.mobileRadioPower + app.gpsPower + app.sensorPower;
- if (DEBUG && totalUnnormalizedPower != 0) {
- Log.d(TAG, String.format("UID %d: total power=%s",
- u.getUid(), makemAh(totalUnnormalizedPower)));
+ final double totalPower = app.sumPower();
+ if (DEBUG && totalPower != 0) {
+ Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(),
+ makemAh(totalPower)));
}
// Add the app to the list if it is consuming power.
- if (totalUnnormalizedPower != 0 || u.getUid() == 0) {
- appList.add(app);
- }
- }
-
- // Fetch real power consumption from hardware.
- double actualTotalWifiPower = 0.0;
- if (mStats.getWifiControllerActivity(BatteryStats.CONTROLLER_ENERGY, mStatsType) != 0) {
- final double kDefaultVoltage = 3.36;
- final long energy = mStats.getWifiControllerActivity(
- BatteryStats.CONTROLLER_ENERGY, mStatsType);
- final double voltage = mPowerProfile.getAveragePowerOrDefault(
- PowerProfile.OPERATING_VOLTAGE_WIFI, kDefaultVoltage);
- actualTotalWifiPower = energy / (voltage * 1000*60*60);
- }
-
- final int appCount = appList.size();
- for (int i = 0; i < appCount; i++) {
- // Normalize power where possible.
- final BatterySipper app = appList.get(i);
- if (actualTotalWifiPower != 0) {
- app.wifiPower = (app.wifiPower / maxWifiPower) * actualTotalWifiPower;
- }
-
- // Assign the final power consumption here.
- final double power = app.wifiPower + app.cpuPower + app.wakeLockPower
- + app.mobileRadioPower + app.gpsPower + app.sensorPower;
- app.values[0] = app.value = power;
-
- //
- // Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list.
- //
-
- final int uid = app.getUid();
- final int userId = UserHandle.getUserId(uid);
- if (uid == Process.WIFI_UID) {
- mWifiSippers.add(app);
- mWifiPower += power;
- } else if (uid == Process.BLUETOOTH_UID) {
- mBluetoothSippers.add(app);
- mBluetoothPower += power;
- } else if (!forAllUsers && asUsers.get(userId) == null
- && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
- // We are told to just report this user's apps as one large entry.
- List<BatterySipper> list = mUserSippers.get(userId);
- if (list == null) {
- list = new ArrayList<>();
- mUserSippers.put(userId, list);
- }
- list.add(app);
-
- Double userPower = mUserPower.get(userId);
- if (userPower == null) {
- userPower = power;
+ if (totalPower != 0 || u.getUid() == 0) {
+ //
+ // Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list.
+ //
+ final int uid = app.getUid();
+ final int userId = UserHandle.getUserId(uid);
+ if (uid == Process.WIFI_UID) {
+ mWifiSippers.add(app);
+ } else if (uid == Process.BLUETOOTH_UID) {
+ mBluetoothSippers.add(app);
+ } else if (!forAllUsers && asUsers.get(userId) == null
+ && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
+ // We are told to just report this user's apps as one large entry.
+ List<BatterySipper> list = mUserSippers.get(userId);
+ if (list == null) {
+ list = new ArrayList<>();
+ mUserSippers.put(userId, list);
+ }
+ list.add(app);
} else {
- userPower += power;
+ mUsageList.add(app);
}
- mUserPower.put(userId, userPower);
- } else {
- mUsageList.add(app);
- if (power > mMaxPower) mMaxPower = power;
- if (power > mMaxRealPower) mMaxRealPower = power;
- mComputedPower += power;
- }
- if (uid == 0) {
- osApp = app;
- }
- }
-
- // The device has probably been awake for longer than the screen on
- // time and application wake lock time would account for. Assign
- // this remainder to the OS, if possible.
- if (osApp != null) {
- long wakeTimeMillis = mBatteryUptime / 1000;
- wakeTimeMillis -= (totalAppWakelockTimeUs / 1000)
- + (mStats.getScreenOnTime(mRawRealtime, which) / 1000);
- if (wakeTimeMillis > 0) {
- double power = (wakeTimeMillis
- * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE))
- / (60*60*1000);
- if (DEBUG) Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power "
- + makemAh(power));
- osApp.wakeLockTime += wakeTimeMillis;
- osApp.value += power;
- osApp.values[0] += power;
- if (osApp.value > mMaxPower) mMaxPower = osApp.value;
- if (osApp.value > mMaxRealPower) mMaxRealPower = osApp.value;
- mComputedPower += power;
+ if (uid == 0) {
+ // The device has probably been awake for longer than the screen on
+ // time and application wake lock time would account for. Assign
+ // this remainder to the OS, if possible.
+ mWakelockPowerCalculator.calculateRemaining(app, mStats, mRawRealtime,
+ mRawUptime, mStatsType);
+ app.sumPower();
+ }
}
}
}
@@ -744,7 +521,7 @@
double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
* phoneOnTimeMs / (60*60*1000);
if (phoneOnPower != 0) {
- BatterySipper bs = addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
+ addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
}
}
@@ -773,54 +550,19 @@
}
private void addRadioUsage() {
- double power = 0;
- final int BINS = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
- long signalTimeMs = 0;
- long noCoverageTimeMs = 0;
- for (int i = 0; i < BINS; i++) {
- long strengthTimeMs = mStats.getPhoneSignalStrengthTime(i, mRawRealtime, mStatsType)
- / 1000;
- double p = (strengthTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ON, i))
- / (60*60*1000);
- if (DEBUG && p != 0) {
- Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
- + makemAh(p));
- }
- power += p;
- signalTimeMs += strengthTimeMs;
- if (i == 0) {
- noCoverageTimeMs = strengthTimeMs;
- }
- }
- long scanningTimeMs = mStats.getPhoneSignalScanningTime(mRawRealtime, mStatsType)
- / 1000;
- double p = (scanningTimeMs * mPowerProfile.getAveragePower(
- PowerProfile.POWER_RADIO_SCANNING))
- / (60*60*1000);
- if (DEBUG && p != 0) {
- Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs + " power=" + makemAh(p));
- }
- power += p;
- long radioActiveTimeUs = mStats.getMobileRadioActiveTime(mRawRealtime, mStatsType);
- long remainingActiveTime = (radioActiveTimeUs - mAppMobileActive) / 1000;
- if (remainingActiveTime > 0) {
- power += getMobilePowerPerMs() * remainingActiveTime;
- }
- if (power != 0) {
- BatterySipper bs =
- addEntry(BatterySipper.DrainType.CELL, signalTimeMs, power);
- if (signalTimeMs != 0) {
- bs.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs;
- }
- bs.mobileActive = remainingActiveTime;
- bs.mobileActiveCount = mStats.getMobileRadioActiveUnknownCount(mStatsType);
+ BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
+ mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtime, mRawUptime,
+ mStatsType);
+ radio.sumPower();
+ if (radio.totalPowerMah > 0) {
+ mUsageList.add(radio);
}
}
private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) {
for (int i=0; i<from.size(); i++) {
BatterySipper wbs = from.get(i);
- if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTime);
+ if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTimeMs);
bs.add(wbs);
}
bs.computeMobilemspp();
@@ -847,41 +589,12 @@
* of WiFi to the WiFi subsystem.
*/
private void addWiFiUsage() {
- final long idleTimeMs = mStats.getWifiControllerActivity(
- BatteryStats.CONTROLLER_IDLE_TIME, mStatsType);
- final long txTimeMs = mStats.getWifiControllerActivity(
- BatteryStats.CONTROLLER_TX_TIME, mStatsType);
- final long rxTimeMs = mStats.getWifiControllerActivity(
- BatteryStats.CONTROLLER_RX_TIME, mStatsType);
- final long energy = mStats.getWifiControllerActivity(
- BatteryStats.CONTROLLER_ENERGY, mStatsType);
- final long totalTimeRunning = idleTimeMs + txTimeMs + rxTimeMs;
-
- double powerDrain = 0;
- if (energy == 0 && totalTimeRunning > 0) {
- // Energy is not reported, which means we may have left over power drain not attributed
- // to any app. Assign this power to the WiFi app.
- // TODO(adamlesinski): This mimics the old behavior. However, mAppWifiRunningTime
- // is the accumulation of the time each app kept the WiFi chip on. Multiple apps
- // can do this at the same time, so these times do not add up to the total time
- // the WiFi chip was on. Consider normalizing the time spent running and calculating
- // power from that? Normalizing the times will assign a weight to each app which
- // should better represent power usage.
- powerDrain = ((totalTimeRunning - mAppWifiRunning)
- * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / (60*60*1000);
- }
-
- if (DEBUG && powerDrain != 0) {
- Log.d(TAG, "Wifi active: time=" + (txTimeMs + rxTimeMs)
- + " power=" + makemAh(powerDrain));
- }
-
- // TODO(adamlesinski): mWifiPower is already added as a BatterySipper...
- // Are we double counting here?
- final double power = mWifiPower + powerDrain;
- if (power > 0) {
- BatterySipper bs = addEntry(BatterySipper.DrainType.WIFI, totalTimeRunning, power);
+ BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);
+ mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, mStatsType);
+ bs.sumPower();
+ if (bs.totalPowerMah > 0 || !mWifiSippers.isEmpty()) {
aggregateSippers(bs, mWifiSippers, "WIFI");
+ mUsageList.add(bs);
}
}
@@ -890,30 +603,10 @@
* Bluetooth Category.
*/
private void addBluetoothUsage() {
- final double kDefaultVoltage = 3.36;
- final long idleTimeMs = mStats.getBluetoothControllerActivity(
- BatteryStats.CONTROLLER_IDLE_TIME, mStatsType);
- final long txTimeMs = mStats.getBluetoothControllerActivity(
- BatteryStats.CONTROLLER_TX_TIME, mStatsType);
- final long rxTimeMs = mStats.getBluetoothControllerActivity(
- BatteryStats.CONTROLLER_RX_TIME, mStatsType);
- final long energy = mStats.getBluetoothControllerActivity(
- BatteryStats.CONTROLLER_ENERGY, mStatsType);
- final double voltage = mPowerProfile.getAveragePowerOrDefault(
- PowerProfile.OPERATING_VOLTAGE_BLUETOOTH, kDefaultVoltage);
-
- // energy is measured in mA * V * ms, and we are interested in mAh
- final double powerDrain = energy / (voltage * 60*60*1000);
-
- if (DEBUG && powerDrain != 0) {
- Log.d(TAG, "Bluetooth active: time=" + (txTimeMs + rxTimeMs)
- + " power=" + makemAh(powerDrain));
- }
-
- final long totalTime = idleTimeMs + txTimeMs + rxTimeMs;
- final double power = mBluetoothPower + powerDrain;
- if (power > 0) {
- BatterySipper bs = addEntry(BatterySipper.DrainType.BLUETOOTH, totalTime, power);
+ BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
+ mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime,
+ mStatsType);
+ if (bs.sumPower() > 0) {
aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
}
}
@@ -928,55 +621,16 @@
}
private void addUserUsage() {
- for (int i=0; i<mUserSippers.size(); i++) {
+ for (int i = 0; i < mUserSippers.size(); i++) {
final int userId = mUserSippers.keyAt(i);
- final List<BatterySipper> sippers = mUserSippers.valueAt(i);
- Double userPower = mUserPower.get(userId);
- double power = (userPower != null) ? userPower : 0.0;
- BatterySipper bs = addEntry(BatterySipper.DrainType.USER, 0, power);
+ BatterySipper bs = new BatterySipper(DrainType.USER, null, 0);
bs.userId = userId;
- aggregateSippers(bs, sippers, "User");
+ aggregateSippers(bs, mUserSippers.valueAt(i), "User");
+ bs.sumPower();
+ mUsageList.add(bs);
}
}
- /**
- * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio.
- */
- private double getMobilePowerPerPacket() {
- final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system
- final double MOBILE_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
- / 3600;
-
- final long mobileRx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType);
- final long mobileTx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType);
- final long mobileData = mobileRx + mobileTx;
-
- final long radioDataUptimeMs
- = mStats.getMobileRadioActiveTime(mRawRealtime, mStatsType) / 1000;
- final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0)
- ? (mobileData / (double)radioDataUptimeMs)
- : (((double)MOBILE_BPS) / 8 / 2048);
-
- return (MOBILE_POWER / mobilePps) / (60*60);
- }
-
- /**
- * Return estimated power (in mAs) of keeping the radio up
- */
- private double getMobilePowerPerMs() {
- return mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) / (60*60*1000);
- }
-
- /**
- * Return estimated power (in mAs) of sending a byte with the Wi-Fi radio.
- */
- private double getWifiPowerPerPacket() {
- final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system
- final double WIFI_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE)
- / 3600;
- return (WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048)) / (60*60);
- }
-
private void processMiscUsage() {
addUserUsage();
addPhoneUsage();
@@ -992,15 +646,10 @@
}
private BatterySipper addEntry(DrainType drainType, long time, double power) {
- mComputedPower += power;
- if (power > mMaxRealPower) mMaxRealPower = power;
- return addEntryNoTotal(drainType, time, power);
- }
-
- private BatterySipper addEntryNoTotal(DrainType drainType, long time, double power) {
- if (power > mMaxPower) mMaxPower = power;
- BatterySipper bs = new BatterySipper(drainType, null, new double[] {power});
- bs.usageTime = time;
+ BatterySipper bs = new BatterySipper(drainType, null, 0);
+ bs.usagePowerMah = power;
+ bs.usageTimeMs = time;
+ bs.sumPower();
mUsageList.add(bs);
return bs;
}
@@ -1015,7 +664,7 @@
public long getStatsPeriod() { return mStatsPeriod; }
- public int getStatsType() { return mStatsType; };
+ public int getStatsType() { return mStatsType; }
public double getMaxPower() { return mMaxPower; }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 05ed3ab..793d0d3 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -55,6 +55,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.Xml;
import android.view.Display;
@@ -95,6 +96,7 @@
public final class BatteryStatsImpl extends BatteryStats {
private static final String TAG = "BatteryStatsImpl";
private static final boolean DEBUG = false;
+ private static final boolean DEBUG_ENERGY = false;
private static final boolean DEBUG_HISTORY = false;
private static final boolean USE_OLD_HISTORY = false; // for debugging.
@@ -182,22 +184,20 @@
// elapsed time by the number of active timers to arrive at that timer's share of the time.
// In order to do this, we must refresh each timer whenever the number of active timers
// changes.
- final ArrayList<StopwatchTimer> mPartialTimers = new ArrayList<StopwatchTimer>();
- final ArrayList<StopwatchTimer> mFullTimers = new ArrayList<StopwatchTimer>();
- final ArrayList<StopwatchTimer> mWindowTimers = new ArrayList<StopwatchTimer>();
- final SparseArray<ArrayList<StopwatchTimer>> mSensorTimers
- = new SparseArray<ArrayList<StopwatchTimer>>();
- final ArrayList<StopwatchTimer> mWifiRunningTimers = new ArrayList<StopwatchTimer>();
- final ArrayList<StopwatchTimer> mFullWifiLockTimers = new ArrayList<StopwatchTimer>();
- final ArrayList<StopwatchTimer> mWifiMulticastTimers = new ArrayList<StopwatchTimer>();
- final ArrayList<StopwatchTimer> mWifiScanTimers = new ArrayList<StopwatchTimer>();
- final SparseArray<ArrayList<StopwatchTimer>> mWifiBatchedScanTimers =
- new SparseArray<ArrayList<StopwatchTimer>>();
- final ArrayList<StopwatchTimer> mAudioTurnedOnTimers = new ArrayList<StopwatchTimer>();
- final ArrayList<StopwatchTimer> mVideoTurnedOnTimers = new ArrayList<StopwatchTimer>();
+ final ArrayList<StopwatchTimer> mPartialTimers = new ArrayList<>();
+ final ArrayList<StopwatchTimer> mFullTimers = new ArrayList<>();
+ final ArrayList<StopwatchTimer> mWindowTimers = new ArrayList<>();
+ final SparseArray<ArrayList<StopwatchTimer>> mSensorTimers = new SparseArray<>();
+ final ArrayList<StopwatchTimer> mWifiRunningTimers = new ArrayList<>();
+ final ArrayList<StopwatchTimer> mFullWifiLockTimers = new ArrayList<>();
+ final ArrayList<StopwatchTimer> mWifiMulticastTimers = new ArrayList<>();
+ final ArrayList<StopwatchTimer> mWifiScanTimers = new ArrayList<>();
+ final SparseArray<ArrayList<StopwatchTimer>> mWifiBatchedScanTimers = new SparseArray<>();
+ final ArrayList<StopwatchTimer> mAudioTurnedOnTimers = new ArrayList<>();
+ final ArrayList<StopwatchTimer> mVideoTurnedOnTimers = new ArrayList<>();
// Last partial timers we use for distributing CPU usage.
- final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<StopwatchTimer>();
+ final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<>();
// These are the objects that will want to do something when the device
// is unplugged from power.
@@ -227,7 +227,7 @@
final HistoryItem mHistoryLastLastWritten = new HistoryItem();
final HistoryItem mHistoryReadTmp = new HistoryItem();
final HistoryItem mHistoryAddTmp = new HistoryItem();
- final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap();
+ final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<>();
String[] mReadHistoryStrings;
int[] mReadHistoryUids;
int mReadHistoryChars;
@@ -450,6 +450,8 @@
private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry();
+ private PowerProfile mPowerProfile;
+
/*
* Holds a SamplingTimer associated with each kernel wakelock name being tracked.
*/
@@ -928,6 +930,12 @@
long mUnpluggedTime;
/**
+ * The total time this timer has been running until the latest mark has been set.
+ * Subtract this from mTotalTime to get the time spent running since the mark was set.
+ */
+ long mTimeBeforeMark;
+
+ /**
* Constructs from a parcel.
* @param type
* @param timeBase
@@ -945,6 +953,7 @@
mLoadedTime = in.readLong();
mLastTime = 0;
mUnpluggedTime = in.readLong();
+ mTimeBeforeMark = in.readLong();
timeBase.add(this);
if (DEBUG) Log.i(TAG, "**** READ TIMER #" + mType + ": mTotalTime=" + mTotalTime);
}
@@ -964,7 +973,7 @@
* so can be completely dropped.
*/
boolean reset(boolean detachIfReset) {
- mTotalTime = mLoadedTime = mLastTime = 0;
+ mTotalTime = mLoadedTime = mLastTime = mTimeBeforeMark = 0;
mCount = mLoadedCount = mLastCount = 0;
if (detachIfReset) {
detach();
@@ -985,8 +994,10 @@
out.writeLong(computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs)));
out.writeLong(mLoadedTime);
out.writeLong(mUnpluggedTime);
+ out.writeLong(mTimeBeforeMark);
}
+ @Override
public void onTimeStarted(long elapsedRealtime, long timeBaseUptime, long baseRealtime) {
if (DEBUG && mType < 0) {
Log.v(TAG, "unplug #" + mType + ": realtime=" + baseRealtime
@@ -1002,6 +1013,7 @@
}
}
+ @Override
public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
if (DEBUG && mType < 0) {
Log.v(TAG, "plug #" + mType + ": realtime=" + baseRealtime
@@ -1055,6 +1067,13 @@
return val;
}
+ @Override
+ public long getTimeSinceMarkLocked(long elapsedRealtimeUs) {
+ long val = computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs));
+ return val - mTimeBeforeMark;
+ }
+
+ @Override
public void logState(Printer pw, String prefix) {
pw.println(prefix + "mCount=" + mCount
+ " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount
@@ -1080,6 +1099,9 @@
mCount = mLoadedCount = in.readInt();
mLastCount = 0;
mUnpluggedCount = mCount;
+
+ // When reading the summary, we set the mark to be the latest information.
+ mTimeBeforeMark = mTotalTime;
}
}
@@ -1475,21 +1497,6 @@
return mNesting > 0;
}
- long checkpointRunningLocked(long elapsedRealtimeMs) {
- if (mNesting > 0) {
- // We are running...
- final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
- if (mTimerPool != null) {
- return refreshTimersLocked(batteryRealtime, mTimerPool, this);
- }
- final long heldTime = batteryRealtime - mUpdateTime;
- mUpdateTime = batteryRealtime;
- mTotalTime += heldTime;
- return heldTime;
- }
- return 0;
- }
-
void stopRunningLocked(long elapsedRealtimeMs) {
// Ignore attempt to stop a timer that isn't running
if (mNesting == 0) {
@@ -1567,6 +1574,7 @@
return mCount;
}
+ @Override
boolean reset(boolean detachIfReset) {
boolean canDetach = mNesting <= 0;
super.reset(canDetach && detachIfReset);
@@ -1577,6 +1585,7 @@
return canDetach;
}
+ @Override
void detach() {
super.detach();
if (mTimerPool != null) {
@@ -1584,10 +1593,31 @@
}
}
+ @Override
void readSummaryFromParcelLocked(Parcel in) {
super.readSummaryFromParcelLocked(in);
mNesting = 0;
}
+
+ /**
+ * Set the mark so that we can query later for the total time the timer has
+ * accumulated since this point. The timer can be running or not.
+ *
+ * @param elapsedRealtimeMs the current elapsed realtime in milliseconds.
+ */
+ public void setMark(long elapsedRealtimeMs) {
+ final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
+ if (mNesting > 0) {
+ // We are running.
+ if (mTimerPool != null) {
+ refreshTimersLocked(batteryRealtime, mTimerPool, this);
+ } else {
+ mTotalTime += batteryRealtime - mUpdateTime;
+ mUpdateTime = batteryRealtime;
+ }
+ }
+ mTimeBeforeMark = mTotalTime;
+ }
}
public abstract class OverflowArrayMap<T> {
@@ -3890,7 +3920,6 @@
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
- scheduleSyncExternalStatsLocked();
}
mWifiFullLockNesting++;
getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked(elapsedRealtime);
@@ -3906,7 +3935,6 @@
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
- scheduleSyncExternalStatsLocked();
}
getUidStatsLocked(uid).noteFullWifiLockReleasedLocked(elapsedRealtime);
}
@@ -3964,7 +3992,6 @@
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
- scheduleSyncExternalStatsLocked();
}
mWifiMulticastNesting++;
getUidStatsLocked(uid).noteWifiMulticastEnabledLocked(elapsedRealtime);
@@ -3980,7 +4007,6 @@
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
- scheduleSyncExternalStatsLocked();
}
getUidStatsLocked(uid).noteWifiMulticastDisabledLocked(elapsedRealtime);
}
@@ -4088,7 +4114,8 @@
// During device boot, qtaguid isn't enabled until after the inital
// loading of battery stats. Now that they're enabled, take our initial
// snapshot for future delta calculation.
- updateMobileRadioStateLocked(SystemClock.elapsedRealtime());
+ final long elapsedRealtimeMs = SystemClock.elapsedRealtime();
+ updateMobileRadioStateLocked(elapsedRealtimeMs);
updateWifiStateLocked(null);
}
@@ -4369,6 +4396,18 @@
LongSamplingCounter mMobileRadioActiveCount;
/**
+ * The amount of time this uid has kept the WiFi controller in idle, tx, and rx mode.
+ */
+ LongSamplingCounter[] mWifiControllerTime =
+ new LongSamplingCounter[NUM_CONTROLLER_ACTIVITY_TYPES];
+
+ /**
+ * The amount of time this uid has kept the Bluetooth controller in idle, tx, and rx mode.
+ */
+ LongSamplingCounter[] mBluetoothControllerTime =
+ new LongSamplingCounter[NUM_CONTROLLER_ACTIVITY_TYPES];
+
+ /**
* The CPU times we had at the last history details update.
*/
long mLastStepUserTime;
@@ -4404,22 +4443,22 @@
/**
* The statistics we have collected for this uid's sensor activations.
*/
- final SparseArray<Sensor> mSensorStats = new SparseArray<Sensor>();
+ final SparseArray<Sensor> mSensorStats = new SparseArray<>();
/**
* The statistics we have collected for this uid's processes.
*/
- final ArrayMap<String, Proc> mProcessStats = new ArrayMap<String, Proc>();
+ final ArrayMap<String, Proc> mProcessStats = new ArrayMap<>();
/**
* The statistics we have collected for this uid's processes.
*/
- final ArrayMap<String, Pkg> mPackageStats = new ArrayMap<String, Pkg>();
+ final ArrayMap<String, Pkg> mPackageStats = new ArrayMap<>();
/**
* The transient wake stats we have collected for this uid's pids.
*/
- final SparseArray<Pid> mPids = new SparseArray<Pid>();
+ final SparseArray<Pid> mPids = new SparseArray<>();
public Uid(int uid) {
mUid = uid;
@@ -4580,6 +4619,13 @@
}
}
+ public void noteWifiControllerActivityLocked(int type, long timeMs) {
+ if (mWifiControllerTime[type] == null) {
+ mWifiControllerTime[type] = new LongSamplingCounter(mOnBatteryTimeBase);
+ }
+ mWifiControllerTime[type].addCountLocked(timeMs);
+ }
+
public StopwatchTimer createAudioTurnedOnTimerLocked() {
if (mAudioTurnedOnTimer == null) {
mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
@@ -4895,6 +4941,15 @@
? (int)mMobileRadioActiveCount.getCountLocked(which) : 0;
}
+ @Override
+ public long getWifiControllerActivity(int type, int which) {
+ if (type >= 0 && type < NUM_CONTROLLER_ACTIVITY_TYPES &&
+ mWifiControllerTime[type] != null) {
+ return mWifiControllerTime[type].getCountLocked(which);
+ }
+ return 0;
+ }
+
void initNetworkActivityLocked() {
mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
mNetworkPacketActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
@@ -4978,6 +5033,16 @@
mMobileRadioActiveCount.reset(false);
}
+ for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
+ if (mWifiControllerTime[i] != null) {
+ mWifiControllerTime[i].reset(false);
+ }
+
+ if (mBluetoothControllerTime[i] != null) {
+ mBluetoothControllerTime[i].reset(false);
+ }
+ }
+
final ArrayMap<String, Wakelock> wakeStats = mWakelockStats.getMap();
for (int iw=wakeStats.size()-1; iw>=0; iw--) {
Wakelock wl = wakeStats.valueAt(iw);
@@ -5100,6 +5165,16 @@
mNetworkPacketActivityCounters[i].detach();
}
}
+
+ for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
+ if (mWifiControllerTime[i] != null) {
+ mWifiControllerTime[i].detach();
+ }
+
+ if (mBluetoothControllerTime[i] != null) {
+ mBluetoothControllerTime[i].detach();
+ }
+ }
mPids.clear();
}
@@ -5189,6 +5264,7 @@
} else {
out.writeInt(0);
}
+
if (mAudioTurnedOnTimer != null) {
out.writeInt(1);
mAudioTurnedOnTimer.writeToParcel(out, elapsedRealtimeUs);
@@ -5240,6 +5316,24 @@
} else {
out.writeInt(0);
}
+
+ for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
+ if (mWifiControllerTime[i] != null) {
+ out.writeInt(1);
+ mWifiControllerTime[i].writeToParcel(out);
+ } else {
+ out.writeInt(0);
+ }
+ }
+
+ for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
+ if (mBluetoothControllerTime[i] != null) {
+ out.writeInt(1);
+ mBluetoothControllerTime[i].writeToParcel(out);
+ } else {
+ out.writeInt(0);
+ }
+ }
}
void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) {
@@ -5389,6 +5483,22 @@
mNetworkByteActivityCounters = null;
mNetworkPacketActivityCounters = null;
}
+
+ for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
+ if (in.readInt() != 0) {
+ mWifiControllerTime[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
+ } else {
+ mWifiControllerTime[i] = null;
+ }
+ }
+
+ for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
+ if (in.readInt() != 0) {
+ mBluetoothControllerTime[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
+ } else {
+ mBluetoothControllerTime[i] = null;
+ }
+ }
}
/**
@@ -6644,6 +6754,12 @@
readFromParcel(p);
}
+ public void setPowerProfile(PowerProfile profile) {
+ synchronized (this) {
+ mPowerProfile = profile;
+ }
+ }
+
public void setCallback(BatteryCallback cb) {
mCallback = cb;
}
@@ -7367,9 +7483,12 @@
* @param info The energy information from the WiFi controller.
*/
public void updateWifiStateLocked(@Nullable final WifiActivityEnergyInfo info) {
- final NetworkStats delta;
+ final long elapsedRealtimeMs = SystemClock.elapsedRealtime();
+ NetworkStats delta = null;
try {
- delta = getNetworkStatsDeltaLocked(mWifiIfaces, mWifiNetworkStats);
+ if (!ArrayUtils.isEmpty(mWifiIfaces)) {
+ delta = getNetworkStatsDeltaLocked(mWifiIfaces, mWifiNetworkStats);
+ }
} catch (IOException e) {
Slog.wtf(TAG, "Failed to get wifi network stats", e);
return;
@@ -7379,14 +7498,19 @@
return;
}
+ SparseLongArray rxPackets = new SparseLongArray();
+ SparseLongArray txPackets = new SparseLongArray();
+ long totalTxPackets = 0;
+ long totalRxPackets = 0;
if (delta != null) {
final int size = delta.size();
for (int i = 0; i < size; i++) {
final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
- if (DEBUG) {
+ if (DEBUG_ENERGY) {
Slog.d(TAG, "Wifi uid " + entry.uid + ": delta rx=" + entry.rxBytes
- + " tx=" + entry.txBytes);
+ + " tx=" + entry.txBytes + " rxPackets=" + entry.rxPackets
+ + " txPackets=" + entry.txPackets);
}
if (entry.rxBytes == 0 || entry.txBytes == 0) {
@@ -7398,6 +7522,13 @@
entry.rxPackets);
u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
entry.txPackets);
+ rxPackets.put(u.getUid(), entry.rxPackets);
+ txPackets.put(u.getUid(), entry.txPackets);
+
+ // Sum the total number of packets so that the Rx Power and Tx Power can
+ // be evenly distributed amongst the apps.
+ totalRxPackets += entry.rxPackets;
+ totalTxPackets += entry.txPackets;
mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
entry.rxBytes);
@@ -7411,6 +7542,119 @@
}
if (info != null) {
+ // Measured in mAms
+ final long txTimeMs = info.getControllerTxTimeMillis();
+ final long rxTimeMs = info.getControllerRxTimeMillis();
+ final long idleTimeMs = info.getControllerIdleTimeMillis();
+ final long totalTimeMs = txTimeMs + rxTimeMs + idleTimeMs;
+
+ long leftOverRxTimeMs = rxTimeMs;
+
+ if (DEBUG_ENERGY) {
+ Slog.d(TAG, "------ BEGIN WiFi power blaming ------");
+ Slog.d(TAG, " Tx Time: " + txTimeMs + " ms");
+ Slog.d(TAG, " Rx Time: " + rxTimeMs + " ms");
+ Slog.d(TAG, " Idle Time: " + idleTimeMs + " ms");
+ Slog.d(TAG, " Total Time: " + totalTimeMs + " ms");
+ }
+
+ long totalWifiLockTimeMs = 0;
+ long totalScanTimeMs = 0;
+
+ // On the first pass, collect some totals so that we can normalize power
+ // calculations if we need to.
+ final int uidStatsSize = mUidStats.size();
+ for (int i = 0; i < uidStatsSize; i++) {
+ final Uid uid = mUidStats.valueAt(i);
+
+ // Sum the total scan power for all apps.
+ totalScanTimeMs += uid.mWifiScanTimer.getTimeSinceMarkLocked(
+ elapsedRealtimeMs * 1000) / 1000;
+
+ // Sum the total time holding wifi lock for all apps.
+ totalWifiLockTimeMs += uid.mFullWifiLockTimer.getTimeSinceMarkLocked(
+ elapsedRealtimeMs * 1000) / 1000;
+ }
+
+ if (DEBUG_ENERGY && totalScanTimeMs > rxTimeMs) {
+ Slog.d(TAG, " !Estimated scan time > Actual rx time (" + totalScanTimeMs + " ms > "
+ + rxTimeMs + " ms). Normalizing scan time.");
+ }
+
+ // Actually assign and distribute power usage to apps.
+ for (int i = 0; i < uidStatsSize; i++) {
+ final Uid uid = mUidStats.valueAt(i);
+
+ long scanTimeSinceMarkMs = uid.mWifiScanTimer.getTimeSinceMarkLocked(
+ elapsedRealtimeMs * 1000) / 1000;
+ if (scanTimeSinceMarkMs > 0) {
+ // Set the new mark so that next time we get new data since this point.
+ uid.mWifiScanTimer.setMark(elapsedRealtimeMs);
+
+ if (totalScanTimeMs > rxTimeMs) {
+ // Our total scan time is more than the reported Rx time.
+ // This is possible because the cost of a scan is approximate.
+ // Let's normalize the result so that we evenly blame each app
+ // scanning.
+ //
+ // This means that we may have apps that received packets not be blamed
+ // for this, but this is fine as scans are relatively more expensive.
+ scanTimeSinceMarkMs = (rxTimeMs * scanTimeSinceMarkMs) / totalScanTimeMs;
+ }
+
+ if (DEBUG_ENERGY) {
+ Slog.d(TAG, " ScanTime for UID " + uid.getUid() + ": "
+ + scanTimeSinceMarkMs + " ms)");
+ }
+ uid.noteWifiControllerActivityLocked(CONTROLLER_RX_TIME, scanTimeSinceMarkMs);
+ leftOverRxTimeMs -= scanTimeSinceMarkMs;
+ }
+
+ // Distribute evenly the power consumed while Idle to each app holding a WiFi
+ // lock.
+ final long wifiLockTimeSinceMarkMs = uid.mFullWifiLockTimer.getTimeSinceMarkLocked(
+ elapsedRealtimeMs * 1000) / 1000;
+ if (wifiLockTimeSinceMarkMs > 0) {
+ // Set the new mark so that next time we get new data since this point.
+ uid.mFullWifiLockTimer.setMark(elapsedRealtimeMs);
+
+ final long myIdleTimeMs = (wifiLockTimeSinceMarkMs * idleTimeMs)
+ / totalWifiLockTimeMs;
+ if (DEBUG_ENERGY) {
+ Slog.d(TAG, " IdleTime for UID " + uid.getUid() + ": "
+ + myIdleTimeMs + " ms");
+ }
+ uid.noteWifiControllerActivityLocked(CONTROLLER_IDLE_TIME, myIdleTimeMs);
+ }
+ }
+
+ if (DEBUG_ENERGY) {
+ Slog.d(TAG, " New RxPower: " + leftOverRxTimeMs + " ms");
+ }
+
+ // Distribute the Tx power appropriately between all apps that transmitted packets.
+ for (int i = 0; i < txPackets.size(); i++) {
+ final Uid uid = getUidStatsLocked(txPackets.keyAt(i));
+ final long myTxTimeMs = (txPackets.valueAt(i) * txTimeMs) / totalTxPackets;
+ if (DEBUG_ENERGY) {
+ Slog.d(TAG, " TxTime for UID " + uid.getUid() + ": " + myTxTimeMs + " ms");
+ }
+ uid.noteWifiControllerActivityLocked(CONTROLLER_TX_TIME, myTxTimeMs);
+ }
+
+ // Distribute the remaining Rx power appropriately between all apps that received
+ // packets.
+ for (int i = 0; i < rxPackets.size(); i++) {
+ final Uid uid = getUidStatsLocked(rxPackets.keyAt(i));
+ final long myRxTimeMs = (rxPackets.valueAt(i) * leftOverRxTimeMs) / totalRxPackets;
+ if (DEBUG_ENERGY) {
+ Slog.d(TAG, " RxTime for UID " + uid.getUid() + ": " + myRxTimeMs + " ms");
+ }
+ uid.noteWifiControllerActivityLocked(CONTROLLER_RX_TIME, myRxTimeMs);
+ }
+
+ // Any left over power use will be picked up by the WiFi category in BatteryStatsHelper.
+
// Update WiFi controller stats.
mWifiActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
info.getControllerRxTimeMillis());
@@ -7418,19 +7662,29 @@
info.getControllerTxTimeMillis());
mWifiActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
info.getControllerIdleTimeMillis());
- mWifiActivityCounters[CONTROLLER_ENERGY].addCountLocked(
- info.getControllerEnergyUsed());
+
+ final double powerDrainMaMs;
+ if (mPowerProfile.getAveragePower(
+ PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE) == 0) {
+ powerDrainMaMs = 0.0;
+ } else {
+ powerDrainMaMs = info.getControllerEnergyUsed()
+ / mPowerProfile.getAveragePower(
+ PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE);
+ }
+ mWifiActivityCounters[CONTROLLER_POWER_DRAIN].addCountLocked((long) powerDrainMaMs);
}
}
/**
* Distribute Cell radio energy info and network traffic to apps.
*/
- public void updateMobileRadioStateLocked(long elapsedRealtimeMs) {
- final NetworkStats delta;
-
+ public void updateMobileRadioStateLocked(final long elapsedRealtimeMs) {
+ NetworkStats delta = null;
try {
- delta = getNetworkStatsDeltaLocked(mMobileIfaces, mMobileNetworkStats);
+ if (!ArrayUtils.isEmpty(mMobileIfaces)) {
+ delta = getNetworkStatsDeltaLocked(mMobileIfaces, mMobileNetworkStats);
+ }
} catch (IOException e) {
Slog.wtf(TAG, "Failed to get mobile network stats", e);
return;
@@ -7440,14 +7694,24 @@
return;
}
- long radioTime = mMobileRadioActivePerAppTimer.checkpointRunningLocked(elapsedRealtimeMs);
+ long radioTime = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
+ elapsedRealtimeMs * 1000);
+ mMobileRadioActivePerAppTimer.setMark(elapsedRealtimeMs);
long totalPackets = delta.getTotalPackets();
final int size = delta.size();
for (int i = 0; i < size; i++) {
final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
- if (entry.rxBytes == 0 || entry.txBytes == 0) continue;
+ if (entry.rxBytes == 0 || entry.txBytes == 0) {
+ continue;
+ }
+
+ if (DEBUG_ENERGY) {
+ Slog.d(TAG, "Mobile uid " + entry.uid + ": delta rx=" + entry.rxBytes
+ + " tx=" + entry.txBytes + " rxPackets=" + entry.rxPackets
+ + " txPackets=" + entry.txPackets);
+ }
final Uid u = getUidStatsLocked(mapUid(entry.uid));
u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_DATA, entry.rxBytes,
@@ -7488,14 +7752,14 @@
* @param info The energy information from the bluetooth controller.
*/
public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info) {
- if (info != null && mOnBatteryInternal) {
+ if (info != null && mOnBatteryInternal && false) {
mBluetoothActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
info.getControllerRxTimeMillis());
mBluetoothActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
info.getControllerTxTimeMillis());
mBluetoothActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
info.getControllerIdleTimeMillis());
- mBluetoothActivityCounters[CONTROLLER_ENERGY].addCountLocked(
+ mBluetoothActivityCounters[CONTROLLER_POWER_DRAIN].addCountLocked(
info.getControllerEnergyUsed());
}
}
diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
new file mode 100644
index 0000000..3557209
--- /dev/null
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import android.os.BatteryStats;
+import android.util.Log;
+
+public class BluetoothPowerCalculator extends PowerCalculator {
+ private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
+ private static final String TAG = "BluetoothPowerCalculator";
+
+ @Override
+ public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ // No per-app distribution yet.
+ }
+
+ @Override
+ public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ final long idleTimeMs = stats.getBluetoothControllerActivity(
+ BatteryStats.CONTROLLER_IDLE_TIME, statsType);
+ final long txTimeMs = stats.getBluetoothControllerActivity(
+ BatteryStats.CONTROLLER_TX_TIME, statsType);
+ final long rxTimeMs = stats.getBluetoothControllerActivity(
+ BatteryStats.CONTROLLER_RX_TIME, statsType);
+ final long powerMaMs = stats.getBluetoothControllerActivity(
+ BatteryStats.CONTROLLER_POWER_DRAIN, statsType);
+ final double powerMah = powerMaMs / (double)(1000*60*60);
+ final long totalTimeMs = idleTimeMs + txTimeMs + rxTimeMs;
+
+ if (DEBUG && powerMah != 0) {
+ Log.d(TAG, "Bluetooth active: time=" + (totalTimeMs)
+ + " power=" + BatteryStatsHelper.makemAh(powerMah));
+ }
+
+ app.usagePowerMah = powerMah;
+ app.usageTimeMs = totalTimeMs;
+ }
+}
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
new file mode 100644
index 0000000..6c3f958
--- /dev/null
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import android.os.BatteryStats;
+import android.util.ArrayMap;
+import android.util.Log;
+
+public class CpuPowerCalculator extends PowerCalculator {
+ private static final String TAG = "CpuPowerCalculator";
+ private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
+
+ private final double[] mPowerCpuNormal;
+
+ /**
+ * Reusable array for calculations.
+ */
+ private final long[] mSpeedStepTimes;
+
+ public CpuPowerCalculator(PowerProfile profile) {
+ final int speedSteps = profile.getNumSpeedSteps();
+ mPowerCpuNormal = new double[speedSteps];
+ mSpeedStepTimes = new long[speedSteps];
+ for (int p = 0; p < speedSteps; p++) {
+ mPowerCpuNormal[p] = profile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p);
+ }
+ }
+
+ @Override
+ public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ final int speedSteps = mSpeedStepTimes.length;
+
+ // Keep track of the package with highest drain.
+ double highestDrain = 0;
+
+ final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
+ final int processStatsCount = processStats.size();
+ for (int i = 0; i < processStatsCount; i++) {
+ final BatteryStats.Uid.Proc ps = processStats.valueAt(i);
+ final String processName = processStats.keyAt(i);
+
+ app.cpuFgTimeMs += ps.getForegroundTime(statsType);
+ final long totalCpuTime = ps.getUserTime(statsType) + ps.getSystemTime(statsType);
+ app.cpuTimeMs += totalCpuTime;
+
+ // Calculate the total CPU time spent at the various speed steps.
+ long totalTimeAtSpeeds = 0;
+ for (int step = 0; step < speedSteps; step++) {
+ mSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, statsType);
+ totalTimeAtSpeeds += mSpeedStepTimes[step];
+ }
+ totalTimeAtSpeeds = Math.max(totalTimeAtSpeeds, 1);
+
+ // Then compute the ratio of time spent at each speed and figure out
+ // the total power consumption.
+ double cpuPower = 0;
+ for (int step = 0; step < speedSteps; step++) {
+ final double ratio = (double) mSpeedStepTimes[step] / totalTimeAtSpeeds;
+ final double cpuSpeedStepPower = ratio * totalCpuTime * mPowerCpuNormal[step];
+ if (DEBUG && ratio != 0) {
+ Log.d(TAG, "UID " + u.getUid() + ": CPU step #"
+ + step + " ratio=" + BatteryStatsHelper.makemAh(ratio) + " power="
+ + BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000)));
+ }
+ cpuPower += cpuSpeedStepPower;
+ }
+
+ if (DEBUG && cpuPower != 0) {
+ Log.d(TAG, String.format("process %s, cpu power=%s",
+ processName, BatteryStatsHelper.makemAh(cpuPower / (60 * 60 * 1000))));
+ }
+ app.cpuPowerMah += cpuPower;
+
+ // Each App can have multiple packages and with multiple running processes.
+ // Keep track of the package who's process has the highest drain.
+ if (app.packageWithHighestDrain == null ||
+ app.packageWithHighestDrain.startsWith("*")) {
+ highestDrain = cpuPower;
+ app.packageWithHighestDrain = processName;
+ } else if (highestDrain < cpuPower && !processName.startsWith("*")) {
+ highestDrain = cpuPower;
+ app.packageWithHighestDrain = processName;
+ }
+ }
+
+ // Ensure that the CPU times make sense.
+ if (app.cpuFgTimeMs > app.cpuTimeMs) {
+ if (DEBUG && app.cpuFgTimeMs > app.cpuTimeMs + 10000) {
+ Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
+ }
+
+ // Statistics may not have been gathered yet.
+ app.cpuTimeMs = app.cpuFgTimeMs;
+ }
+
+ // Convert the CPU power to mAh
+ app.cpuPowerMah /= (60 * 60 * 1000);
+ }
+}
diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
new file mode 100644
index 0000000..9711c3b
--- /dev/null
+++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import android.os.BatteryStats;
+import android.telephony.SignalStrength;
+import android.util.Log;
+
+public class MobileRadioPowerCalculator extends PowerCalculator {
+ private static final String TAG = "MobileRadioPowerController";
+ private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
+ private final double mPowerRadioOn;
+ private final double[] mPowerBins = new double[SignalStrength.NUM_SIGNAL_STRENGTH_BINS];
+ private final double mPowerScan;
+ private BatteryStats mStats;
+ private long mTotalAppMobileActiveMs = 0;
+
+ /**
+ * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio.
+ */
+ private double getMobilePowerPerPacket(long rawRealtimeUs, int statsType) {
+ final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system
+ final double MOBILE_POWER = mPowerRadioOn / 3600;
+
+ final long mobileRx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
+ statsType);
+ final long mobileTx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
+ statsType);
+ final long mobileData = mobileRx + mobileTx;
+
+ final long radioDataUptimeMs =
+ mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
+ final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0)
+ ? (mobileData / (double)radioDataUptimeMs)
+ : (((double)MOBILE_BPS) / 8 / 2048);
+ return (MOBILE_POWER / mobilePps) / (60*60);
+ }
+
+ public MobileRadioPowerCalculator(PowerProfile profile, BatteryStats stats) {
+ mPowerRadioOn = profile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE);
+ for (int i = 0; i < mPowerBins.length; i++) {
+ mPowerBins[i] = profile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE, i);
+ }
+ mPowerScan = profile.getAveragePower(PowerProfile.POWER_RADIO_SCANNING);
+ mStats = stats;
+ }
+
+ @Override
+ public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ // Add cost of mobile traffic.
+ app.mobileRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
+ statsType);
+ app.mobileTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
+ statsType);
+ app.mobileActive = u.getMobileRadioActiveTime(statsType) / 1000;
+ app.mobileActiveCount = u.getMobileRadioActiveCount(statsType);
+ app.mobileRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_RX_DATA,
+ statsType);
+ app.mobileTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_TX_DATA,
+ statsType);
+
+ if (app.mobileActive > 0) {
+ // We are tracking when the radio is up, so can use the active time to
+ // determine power use.
+ mTotalAppMobileActiveMs += app.mobileActive;
+ app.mobileRadioPowerMah = (app.mobileActive * mPowerRadioOn) / (1000*60*60);
+ } else {
+ // We are not tracking when the radio is up, so must approximate power use
+ // based on the number of packets.
+ app.mobileRadioPowerMah = (app.mobileRxPackets + app.mobileTxPackets)
+ * getMobilePowerPerPacket(rawRealtimeUs, statsType);
+ }
+ if (DEBUG && app.mobileRadioPowerMah != 0) {
+ Log.d(TAG, "UID " + u.getUid() + ": mobile packets "
+ + (app.mobileRxPackets + app.mobileTxPackets)
+ + " active time " + app.mobileActive
+ + " power=" + BatteryStatsHelper.makemAh(app.mobileRadioPowerMah));
+ }
+ }
+
+ @Override
+ public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ double power = 0;
+ long signalTimeMs = 0;
+ long noCoverageTimeMs = 0;
+ for (int i = 0; i < mPowerBins.length; i++) {
+ long strengthTimeMs = stats.getPhoneSignalStrengthTime(i, rawRealtimeUs, statsType)
+ / 1000;
+ final double p = (strengthTimeMs * mPowerBins[i]) / (60*60*1000);
+ if (DEBUG && p != 0) {
+ Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
+ + BatteryStatsHelper.makemAh(p));
+ }
+ power += p;
+ signalTimeMs += strengthTimeMs;
+ if (i == 0) {
+ noCoverageTimeMs = strengthTimeMs;
+ }
+ }
+
+ final long scanningTimeMs = stats.getPhoneSignalScanningTime(rawRealtimeUs, statsType)
+ / 1000;
+ final double p = (scanningTimeMs * mPowerScan) / (60*60*1000);
+ if (DEBUG && p != 0) {
+ Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs
+ + " power=" + BatteryStatsHelper.makemAh(p));
+ }
+ power += p;
+ long radioActiveTimeMs = mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
+ long remainingActiveTimeMs = radioActiveTimeMs - mTotalAppMobileActiveMs;
+ if (remainingActiveTimeMs > 0) {
+ power += (mPowerRadioOn * remainingActiveTimeMs) / (1000*60*60);
+ }
+
+ if (power != 0) {
+ app.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs;
+ app.mobileActive = remainingActiveTimeMs;
+ app.mobileActiveCount = stats.getMobileRadioActiveUnknownCount(statsType);
+ app.mobileRadioPowerMah = power;
+ }
+ }
+
+ @Override
+ public void reset() {
+ mTotalAppMobileActiveMs = 0;
+ }
+
+ public void reset(BatteryStats stats) {
+ reset();
+ mStats = stats;
+ }
+}
diff --git a/core/java/com/android/internal/os/PowerCalculator.java b/core/java/com/android/internal/os/PowerCalculator.java
new file mode 100644
index 0000000..cd69d68
--- /dev/null
+++ b/core/java/com/android/internal/os/PowerCalculator.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import android.os.BatteryStats;
+
+/**
+ * Calculates power use of a device subsystem for an app.
+ */
+public abstract class PowerCalculator {
+ /**
+ * Calculate the amount of power an app used for this subsystem.
+ * @param app The BatterySipper that represents the power use of an app.
+ * @param u The recorded stats for the app.
+ * @param rawRealtimeUs The raw system realtime in microseconds.
+ * @param rawUptimeUs The raw system uptime in microseconds.
+ * @param statsType The type of stats. Can be {@link BatteryStats#STATS_CURRENT},
+ * {@link BatteryStats#STATS_SINCE_CHARGED}, or
+ * {@link BatteryStats#STATS_SINCE_UNPLUGGED}.
+ */
+ public abstract void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+ long rawUptimeUs, int statsType);
+
+ /**
+ * Calculate the remaining power that can not be attributed to an app.
+ * @param app The BatterySipper that will represent this remaining power.
+ * @param stats The BatteryStats object from which to retrieve data.
+ * @param rawRealtimeUs The raw system realtime in microseconds.
+ * @param rawUptimeUs The raw system uptime in microseconds.
+ * @param statsType The type of stats. Can be {@link BatteryStats#STATS_CURRENT},
+ * {@link BatteryStats#STATS_SINCE_CHARGED}, or
+ * {@link BatteryStats#STATS_SINCE_UNPLUGGED}.
+ */
+ public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ }
+
+ /**
+ * Reset any state maintained in this calculator.
+ */
+ public void reset() {
+ }
+}
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 944eb5a..7e6706c 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -18,6 +18,7 @@
import android.content.Context;
+import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import com.android.internal.util.XmlUtils;
@@ -75,10 +76,21 @@
*/
public static final String POWER_WIFI_ACTIVE = "wifi.active";
- /**
- * Operating voltage of the WiFi controller.
- */
- public static final String OPERATING_VOLTAGE_WIFI = "wifi.voltage";
+ //
+ // Updated power constants. These are not estimated, they are real world
+ // currents and voltages for the underlying bluetooth and wifi controllers.
+ //
+
+ public static final String POWER_WIFI_CONTROLLER_IDLE = "wifi.controller.idle";
+ public static final String POWER_WIFI_CONTROLLER_RX = "wifi.controller.rx";
+ public static final String POWER_WIFI_CONTROLLER_TX = "wifi.controller.tx";
+ public static final String POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE = "wifi.controller.voltage";
+
+ public static final String POWER_BLUETOOTH_CONTROLLER_IDLE = "bluetooth.controller.idle";
+ public static final String POWER_BLUETOOTH_CONTROLLER_RX = "bluetooth.controller.rx";
+ public static final String POWER_BLUETOOTH_CONTROLLER_TX = "bluetooth.controller.tx";
+ public static final String POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE =
+ "bluetooth.controller.voltage";
/**
* Power consumption when GPS is on.
@@ -100,10 +112,6 @@
*/
public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at";
- /**
- * Operating voltage of the Bluetooth controller.
- */
- public static final String OPERATING_VOLTAGE_BLUETOOTH = "bluetooth.voltage";
/**
* Power consumption when screen is on, not including the backlight power.
@@ -162,7 +170,7 @@
*/
public static final String POWER_BATTERY_CAPACITY = "battery.capacity";
- static final HashMap<String, Object> sPowerMap = new HashMap<String, Object>();
+ static final HashMap<String, Object> sPowerMap = new HashMap<>();
private static final String TAG_DEVICE = "device";
private static final String TAG_ITEM = "item";
@@ -180,7 +188,8 @@
private void readPowerValuesFromXml(Context context) {
int id = com.android.internal.R.xml.power_profile;
- XmlResourceParser parser = context.getResources().getXml(id);
+ final Resources resources = context.getResources();
+ XmlResourceParser parser = resources.getXml(id);
boolean parsingArray = false;
ArrayList<Double> array = new ArrayList<Double>();
String arrayName = null;
@@ -231,6 +240,36 @@
} finally {
parser.close();
}
+
+ // Now collect other config variables.
+ int[] configResIds = new int[] {
+ com.android.internal.R.integer.config_bluetooth_idle_cur_ma,
+ com.android.internal.R.integer.config_bluetooth_rx_cur_ma,
+ com.android.internal.R.integer.config_bluetooth_tx_cur_ma,
+ com.android.internal.R.integer.config_bluetooth_operating_voltage_mv,
+ com.android.internal.R.integer.config_wifi_idle_receive_cur_ma,
+ com.android.internal.R.integer.config_wifi_active_rx_cur_ma,
+ com.android.internal.R.integer.config_wifi_tx_cur_ma,
+ com.android.internal.R.integer.config_wifi_operating_voltage_mv,
+ };
+
+ String[] configResIdKeys = new String[] {
+ POWER_BLUETOOTH_CONTROLLER_IDLE,
+ POWER_BLUETOOTH_CONTROLLER_RX,
+ POWER_BLUETOOTH_CONTROLLER_TX,
+ POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE,
+ POWER_WIFI_CONTROLLER_IDLE,
+ POWER_WIFI_CONTROLLER_RX,
+ POWER_WIFI_CONTROLLER_TX,
+ POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE,
+ };
+
+ for (int i = 0; i < configResIds.length; i++) {
+ int value = resources.getInteger(configResIds[i]);
+ if (value > 0) {
+ sPowerMap.put(configResIdKeys[i], (double) value);
+ }
+ }
}
/**
diff --git a/core/java/com/android/internal/os/SensorPowerCalculator.java b/core/java/com/android/internal/os/SensorPowerCalculator.java
new file mode 100644
index 0000000..c98639b
--- /dev/null
+++ b/core/java/com/android/internal/os/SensorPowerCalculator.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.os.BatteryStats;
+import android.util.SparseArray;
+
+import java.util.List;
+
+public class SensorPowerCalculator extends PowerCalculator {
+ private final List<Sensor> mSensors;
+ private final double mGpsPowerOn;
+
+ public SensorPowerCalculator(PowerProfile profile, SensorManager sensorManager) {
+ mSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
+ mGpsPowerOn = profile.getAveragePower(PowerProfile.POWER_GPS_ON);
+ }
+
+ @Override
+ public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ // Process Sensor usage
+ final SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
+ final int NSE = sensorStats.size();
+ for (int ise = 0; ise < NSE; ise++) {
+ final BatteryStats.Uid.Sensor sensor = sensorStats.valueAt(ise);
+ final int sensorHandle = sensorStats.keyAt(ise);
+ final BatteryStats.Timer timer = sensor.getSensorTime();
+ final long sensorTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
+ switch (sensorHandle) {
+ case BatteryStats.Uid.Sensor.GPS:
+ app.gpsTimeMs = sensorTime;
+ app.gpsPowerMah = (app.gpsTimeMs * mGpsPowerOn) / (1000*60*60);
+ break;
+ default:
+ final int sensorsCount = mSensors.size();
+ for (int i = 0; i < sensorsCount; i++) {
+ final Sensor s = mSensors.get(i);
+ if (s.getHandle() == sensorHandle) {
+ app.sensorPowerMah += (sensorTime * s.getPower()) / (1000*60*60);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/WakelockPowerCalculator.java b/core/java/com/android/internal/os/WakelockPowerCalculator.java
new file mode 100644
index 0000000..7575010f
--- /dev/null
+++ b/core/java/com/android/internal/os/WakelockPowerCalculator.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import android.os.BatteryStats;
+import android.util.ArrayMap;
+import android.util.Log;
+
+public class WakelockPowerCalculator extends PowerCalculator {
+ private static final String TAG = "WakelockPowerCalculator";
+ private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
+ private final double mPowerWakelock;
+ private long mTotalAppWakelockTimeMs = 0;
+
+ public WakelockPowerCalculator(PowerProfile profile) {
+ mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_AWAKE);
+ }
+
+ @Override
+ public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawUptimeUs,
+ long rawRealtimeUs, int statsType) {
+ long wakeLockTimeUs = 0;
+ final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
+ u.getWakelockStats();
+ final int wakelockStatsCount = wakelockStats.size();
+ for (int i = 0; i < wakelockStatsCount; i++) {
+ final BatteryStats.Uid.Wakelock wakelock = wakelockStats.valueAt(i);
+
+ // Only care about partial wake locks since full wake locks
+ // are canceled when the user turns the screen off.
+ BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
+ if (timer != null) {
+ wakeLockTimeUs += timer.getTotalTimeLocked(rawRealtimeUs, statsType);
+ }
+ }
+ app.wakeLockTimeMs = wakeLockTimeUs / 1000; // convert to millis
+ mTotalAppWakelockTimeMs += app.wakeLockTimeMs;
+
+ // Add cost of holding a wake lock.
+ app.wakeLockPowerMah = (app.wakeLockTimeMs * mPowerWakelock) / (1000*60*60);
+ if (DEBUG && app.wakeLockPowerMah != 0) {
+ Log.d(TAG, "UID " + u.getUid() + ": wake " + app.wakeLockTimeMs
+ + " power=" + BatteryStatsHelper.makemAh(app.wakeLockPowerMah));
+ }
+ }
+
+ @Override
+ public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ long wakeTimeMillis = stats.getBatteryUptime(rawUptimeUs) / 1000;
+ wakeTimeMillis -= mTotalAppWakelockTimeMs
+ + (stats.getScreenOnTime(rawRealtimeUs, statsType) / 1000);
+ if (wakeTimeMillis > 0) {
+ final double power = (wakeTimeMillis * mPowerWakelock) / (1000*60*60);
+ if (DEBUG) {
+ Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power "
+ + BatteryStatsHelper.makemAh(power));
+ }
+ app.wakeLockTimeMs += wakeTimeMillis;
+ app.wakeLockPowerMah += power;
+ }
+ }
+
+ @Override
+ public void reset() {
+ mTotalAppWakelockTimeMs = 0;
+ }
+}
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
new file mode 100644
index 0000000..4e77f6b
--- /dev/null
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import android.os.BatteryStats;
+
+/**
+ * WiFi power calculator for when BatteryStats supports energy reporting
+ * from the WiFi controller.
+ */
+public class WifiPowerCalculator extends PowerCalculator {
+ private final double mIdleCurrentMa;
+ private final double mTxCurrentMa;
+ private final double mRxCurrentMa;
+ private double mTotalAppPowerDrain = 0;
+
+ public WifiPowerCalculator(PowerProfile profile) {
+ mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE);
+ mTxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX);
+ mRxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX);
+ }
+
+ @Override
+ public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ final long idleTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME,
+ statsType);
+ final long txTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_TX_TIME, statsType);
+ final long rxTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_RX_TIME, statsType);
+ app.wifiRunningTimeMs = idleTime + rxTime + txTime;
+ app.wifiPowerMah =
+ ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa))
+ / (1000*60*60);
+ mTotalAppPowerDrain += app.wifiPowerMah;
+
+ app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,
+ statsType);
+ app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,
+ statsType);
+ app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,
+ statsType);
+ app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,
+ statsType);
+ }
+
+ @Override
+ public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ final long idleTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME,
+ statsType);
+ final long rxTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_RX_TIME,
+ statsType);
+ final long txTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_TX_TIME,
+ statsType);
+ app.wifiRunningTimeMs = idleTimeMs + rxTimeMs + txTimeMs;
+
+ double powerDrain = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_POWER_DRAIN,
+ statsType) / (1000*60*60);
+ if (powerDrain == 0) {
+ // Some controllers do not report power drain, so we can calculate it here.
+ powerDrain = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa)
+ + (rxTimeMs * mRxCurrentMa)) / (1000*60*60);
+ }
+ app.wifiPowerMah = Math.max(0, powerDrain - mTotalAppPowerDrain);
+ }
+
+ @Override
+ public void reset() {
+ mTotalAppPowerDrain = 0;
+ }
+}
diff --git a/core/java/com/android/internal/os/WifiPowerEstimator.java b/core/java/com/android/internal/os/WifiPowerEstimator.java
new file mode 100644
index 0000000..0172367
--- /dev/null
+++ b/core/java/com/android/internal/os/WifiPowerEstimator.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import android.os.BatteryStats;
+
+/**
+ * Estimates WiFi power usage based on timers in BatteryStats.
+ */
+public class WifiPowerEstimator extends PowerCalculator {
+ private final double mWifiPowerPerPacket;
+ private final double mWifiPowerOn;
+ private final double mWifiPowerScan;
+ private final double mWifiPowerBatchScan;
+ private long mTotalAppWifiRunningTimeMs = 0;
+
+ public WifiPowerEstimator(PowerProfile profile) {
+ mWifiPowerPerPacket = getWifiPowerPerPacket(profile);
+ mWifiPowerOn = profile.getAveragePower(PowerProfile.POWER_WIFI_ON);
+ mWifiPowerScan = profile.getAveragePower(PowerProfile.POWER_WIFI_SCAN);
+ mWifiPowerBatchScan = profile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN);
+ }
+
+ /**
+ * Return estimated power (in mAs) of sending a byte with the Wi-Fi radio.
+ */
+ private static double getWifiPowerPerPacket(PowerProfile profile) {
+ final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system
+ final double WIFI_POWER = profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE)
+ / 3600;
+ return (WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048)) / (60*60);
+ }
+
+ @Override
+ public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,
+ statsType);
+ app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,
+ statsType);
+ app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,
+ statsType);
+ app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,
+ statsType);
+
+ final double wifiPacketPower = (app.wifiRxPackets + app.wifiTxPackets)
+ * mWifiPowerPerPacket;
+
+ app.wifiRunningTimeMs = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;
+ mTotalAppWifiRunningTimeMs += app.wifiRunningTimeMs;
+ final double wifiLockPower = (app.wifiRunningTimeMs * mWifiPowerOn) / (1000*60*60);
+
+ final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType);
+ final double wifiScanPower = (wifiScanTimeMs * mWifiPowerScan) / (1000*60*60);
+
+ double wifiBatchScanPower = 0;
+ for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
+ final long batchScanTimeMs =
+ u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;
+ final double batchScanPower = (batchScanTimeMs * mWifiPowerBatchScan) / (1000*60*60);
+ wifiBatchScanPower += batchScanPower;
+ }
+
+ app.wifiPowerMah = wifiPacketPower + wifiLockPower + wifiScanPower + wifiBatchScanPower;
+ }
+
+ @Override
+ public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ final long totalRunningTimeMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType)
+ / 1000;
+ final double powerDrain = ((totalRunningTimeMs - mTotalAppWifiRunningTimeMs) * mWifiPowerOn)
+ / (1000*60*60);
+ app.wifiRunningTimeMs = totalRunningTimeMs;
+ app.wifiPowerMah = Math.max(0, powerDrain);
+ }
+
+ @Override
+ public void reset() {
+ mTotalAppWifiRunningTimeMs = 0;
+ }
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index eb394c3..1ac1c8a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1487,7 +1487,7 @@
NetworkCapabilities.TRANSPORT_WIFI)) {
timeout = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
- 0);
+ 5);
type = ConnectivityManager.TYPE_WIFI;
} else {
// do not track any other networks
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 7b542be..b5b62b4 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -210,6 +210,7 @@
private boolean mMobileActivityFromRadio = false;
private int mLastPowerStateFromRadio = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+ private int mLastPowerStateFromWifi = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
private final RemoteCallbackList<INetworkActivityListener> mNetworkActivityListeners =
new RemoteCallbackList<INetworkActivityListener>();
@@ -434,6 +435,16 @@
}
}
+ if (ConnectivityManager.isNetworkTypeWifi(type)) {
+ if (mLastPowerStateFromWifi != powerState) {
+ mLastPowerStateFromWifi = powerState;
+ try {
+ getBatteryStats().noteWifiRadioPowerState(powerState, tsNanos);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
boolean isActive = powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM
|| powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index c8db3be..ac70d88 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -40,6 +40,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.util.Slog;
@@ -122,6 +123,7 @@
mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout)
* 1000L);
+ mStats.setPowerProfile(new PowerProfile(context));
}
/**
@@ -541,6 +543,15 @@
}
}
+ @Override
+ public void noteWifiRadioPowerState(int powerState, long tsNanos) {
+ enforceCallingPermission();
+
+ // There was a change in WiFi power state.
+ // Collect data now for the past activity.
+ mHandler.scheduleSync();
+ }
+
public void noteWifiRunning(WorkSource ws) {
enforceCallingPermission();
synchronized (mStats) {
@@ -1095,13 +1106,29 @@
result.mTimestamp = info.getTimeStamp();
result.mStackState = info.getStackState();
result.mControllerTxTimeMs =
- info.getControllerTxTimeMillis()- mLastInfo.mControllerTxTimeMs;
+ info.mControllerTxTimeMs - mLastInfo.mControllerTxTimeMs;
result.mControllerRxTimeMs =
- info.getControllerRxTimeMillis() - mLastInfo.mControllerRxTimeMs;
- result.mControllerIdleTimeMs =
- info.getControllerIdleTimeMillis() - mLastInfo.mControllerIdleTimeMs;
+ info.mControllerRxTimeMs - mLastInfo.mControllerRxTimeMs;
result.mControllerEnergyUsed =
- info.getControllerEnergyUsed() - mLastInfo.mControllerEnergyUsed;
+ info.mControllerEnergyUsed - mLastInfo.mControllerEnergyUsed;
+
+ // WiFi calculates the idle time as a difference from the on time and the various
+ // Rx + Tx times. There seems to be some missing time there because this sometimes
+ // becomes negative. Just cap it at 0 and move on.
+ result.mControllerIdleTimeMs =
+ Math.max(0, info.mControllerIdleTimeMs - mLastInfo.mControllerIdleTimeMs);
+
+ if (result.mControllerTxTimeMs < 0 ||
+ result.mControllerRxTimeMs < 0) {
+ // The stats were reset by the WiFi system (which is why our delta is negative).
+ // Returns the unaltered stats.
+ result.mControllerEnergyUsed = info.mControllerEnergyUsed;
+ result.mControllerRxTimeMs = info.mControllerRxTimeMs;
+ result.mControllerTxTimeMs = info.mControllerTxTimeMs;
+ result.mControllerIdleTimeMs = info.mControllerIdleTimeMs;
+
+ Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + result);
+ }
mLastInfo = info;
return result;
}
@@ -1133,6 +1160,12 @@
*/
void updateExternalStats() {
synchronized (mExternalStatsLock) {
+ if (mContext == null) {
+ // We haven't started yet (which means the BatteryStatsImpl object has
+ // no power profile. Don't consume data we can't compute yet.
+ return;
+ }
+
final WifiActivityEnergyInfo wifiEnergyInfo = pullWifiEnergyInfoLocked();
final BluetoothActivityEnergyInfo bluetoothEnergyInfo = pullBluetoothEnergyInfoLocked();
synchronized (mStats) {