From 9adb9c3b10991ef315c270993f4155709c8a232d Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 13 Aug 2010 14:09:56 -0700 Subject: Various battery info things: - Now track wake locks in battery history. - Now track sensors in battery history. - Some filtering of sensory data. - Fixes to some data that wasn't cleared when resetting battery stats. - Print amount discharged since last charge. And the big part -- keep track of wake locks held per process, and kill processes that hold wake locks too much while they are in the background. This includes information in the battery stats about the process being killed, which will be available to the developer if the app is reported. Change-Id: I97202e94d00aafe0526ba2db74a03212e7539c54 --- core/java/android/app/ActivityManager.java | 6 +- core/java/android/os/BatteryStats.java | 47 +++- .../com/android/internal/app/IBatteryStats.aidl | 4 +- .../com/android/internal/os/BatteryStatsImpl.java | 285 ++++++++++++++++++--- 4 files changed, 305 insertions(+), 37 deletions(-) (limited to 'core/java') diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index d66e98b..d5741fc 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -365,7 +365,8 @@ public class ActivityManager { /** * The time when the service was first made active, either by someone - * starting or binding to it. + * starting or binding to it. This + * is in units of {@link android.os.SystemClock#elapsedRealtime()}. */ public long activeSince; @@ -387,7 +388,8 @@ public class ActivityManager { /** * The time when there was last activity in the service (either - * explicit requests to start it or clients binding to it). + * explicit requests to start it or clients binding to it). This + * is in units of {@link android.os.SystemClock#uptimeMillis()}. */ public long lastActivityTime; diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index a699388..a0a3bdf 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -290,6 +290,11 @@ public abstract class BatteryStats implements Parcelable { */ public static abstract class Proc { + public static class ExcessiveWake { + public long overTime; + public long usedTime; + } + /** * Returns the total time (in 1/100 sec) spent executing in user code. * @@ -326,6 +331,10 @@ public abstract class BatteryStats implements Parcelable { * @see BatteryStats#getCpuSpeedSteps() */ public abstract long getTimeAtCpuSpeedStep(int speedStep, int which); + + public abstract int countExcessiveWakes(); + + public abstract ExcessiveWake getExcessiveWake(int i); } /** @@ -421,6 +430,8 @@ public abstract class BatteryStats implements Parcelable { public static final int STATE_BLUETOOTH_ON_FLAG = 1<<20; public static final int STATE_AUDIO_ON_FLAG = 1<<19; public static final int STATE_VIDEO_ON_FLAG = 1<<18; + public static final int STATE_WAKE_LOCK_FLAG = 1<<17; + public static final int STATE_SENSOR_ON_FLAG = 1<<16; public int states; @@ -470,6 +481,16 @@ public abstract class BatteryStats implements Parcelable { batteryVoltage = o.batteryVoltage; states = o.states; } + + public boolean same(HistoryItem o) { + return batteryLevel == o.batteryLevel + && batteryStatus == o.batteryStatus + && batteryHealth == o.batteryHealth + && batteryPlugType == o.batteryPlugType + && batteryTemperature == o.batteryTemperature + && batteryVoltage == o.batteryVoltage + && states == o.states; + } } public static final class BitDescription { @@ -633,6 +654,8 @@ public abstract class BatteryStats implements Parcelable { new BitDescription(HistoryItem.STATE_BLUETOOTH_ON_FLAG, "bluetooth"), new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio"), new BitDescription(HistoryItem.STATE_VIDEO_ON_FLAG, "video"), + new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock"), + new BitDescription(HistoryItem.STATE_SENSOR_ON_FLAG, "sensor"), new BitDescription(HistoryItem.STATE_BRIGHTNESS_MASK, HistoryItem.STATE_BRIGHTNESS_SHIFT, "brightness", SCREEN_BRIGHTNESS_NAMES), @@ -1376,7 +1399,6 @@ public abstract class BatteryStats implements Parcelable { pw.println(getDischargeStartLevel()); pw.print(prefix); pw.print(" Discharge cycle current level: "); pw.println(getDischargeCurrentLevel()); - } else { pw.print(prefix); pw.println(" Device is currently plugged into power"); pw.print(prefix); pw.print(" Last discharge cycle start level: "); pw.println(getDischargeStartLevel()); @@ -1384,6 +1406,13 @@ public abstract class BatteryStats implements Parcelable { pw.println(getDischargeCurrentLevel()); } pw.println(" "); + } else { + pw.print(prefix); pw.println(" Device battery use since last full charge"); + pw.print(prefix); pw.print(" Amount discharged (lower bound): "); + pw.println(getLowDischargeAmountSinceCharge()); + pw.print(prefix); pw.print(" Amount discharged (upper bound): "); + pw.println(getHighDischargeAmountSinceCharge()); + pw.println(" "); } @@ -1524,12 +1553,16 @@ public abstract class BatteryStats implements Parcelable { long userTime; long systemTime; int starts; + int numExcessive; userTime = ps.getUserTime(which); systemTime = ps.getSystemTime(which); starts = ps.getStarts(which); + numExcessive = which == STATS_SINCE_CHARGED + ? ps.countExcessiveWakes() : 0; - if (userTime != 0 || systemTime != 0 || starts != 0) { + if (userTime != 0 || systemTime != 0 || starts != 0 + || numExcessive != 0) { sb.setLength(0); sb.append(prefix); sb.append(" Proc "); sb.append(ent.getKey()); sb.append(":\n"); @@ -1539,6 +1572,16 @@ public abstract class BatteryStats implements Parcelable { sb.append(prefix); sb.append(" "); sb.append(starts); sb.append(" proc starts"); pw.println(sb.toString()); + for (int e=0; e 0) { + mUpdateTime = stats.getBatteryRealtimeLocked( + SystemClock.elapsedRealtime() * 1000); + } + mAcquireTime = mTotalTime; return canDetach; } @@ -1113,6 +1120,26 @@ public final class BatteryStatsImpl extends BatteryStats { if (!mHaveBatteryLevel || !mRecordingHistory) { return; } + + // If the current time is basically the same as the last time, + // just collapse into one record. + if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE + && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+100)) { + // If the current is the same as the one before, then we no + // longer need the entry. + if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE + && mHistoryLastEnd.same(mHistoryCur)) { + mHistoryLastEnd.next = null; + mHistoryEnd.next = mHistoryCache; + mHistoryCache = mHistoryEnd; + mHistoryEnd = mHistoryLastEnd; + mHistoryLastEnd = null; + } else { + mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, mHistoryCur); + } + return; + } + if (mNumHistoryItems >= MAX_HISTORY_ITEMS) { // Once we've reached the maximum number of items, we only // record changes to the battery level. @@ -1121,6 +1148,7 @@ public final class BatteryStatsImpl extends BatteryStats { return; } } + addHistoryRecordLocked(curTime, HistoryItem.CMD_UPDATE); } @@ -1139,6 +1167,7 @@ public final class BatteryStatsImpl extends BatteryStats { void addHistoryRecordLocked(HistoryItem rec) { mNumHistoryItems++; rec.next = null; + mHistoryLastEnd = mHistoryEnd; if (mHistoryEnd != null) { mHistoryEnd.next = rec; mHistoryEnd = rec; @@ -1151,7 +1180,7 @@ public final class BatteryStatsImpl extends BatteryStats { if (mHistory != null) { mHistoryEnd.next = mHistoryCache; mHistoryCache = mHistory; - mHistory = mHistoryEnd = null; + mHistory = mHistoryLastEnd = mHistoryEnd = null; } mNumHistoryItems = 0; mHistoryBaseTime = 0; @@ -1209,6 +1238,83 @@ public final class BatteryStatsImpl extends BatteryStats { mBluetoothPingStart = -1; } + int mWakeLockNesting; + + public void noteStartWakeLocked(int uid, int pid, String name, int type) { + if (mWakeLockNesting == 0) { + mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG; + if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: " + + Integer.toHexString(mHistoryCur.states)); + addHistoryRecordLocked(SystemClock.elapsedRealtime()); + } + mWakeLockNesting++; + if (uid >= 0) { + getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type); + } + } + + public void noteStopWakeLocked(int uid, int pid, String name, int type) { + mWakeLockNesting--; + if (mWakeLockNesting == 0) { + mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG; + if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: " + + Integer.toHexString(mHistoryCur.states)); + addHistoryRecordLocked(SystemClock.elapsedRealtime()); + } + if (uid >= 0) { + getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type); + } + } + + public void noteProcessDiedLocked(int uid, int pid) { + Uid u = mUidStats.get(uid); + if (u != null) { + u.mPids.remove(pid); + } + } + + public long getProcessWakeTime(int uid, int pid, long realtime) { + Uid u = mUidStats.get(uid); + if (u != null) { + Uid.Pid p = u.mPids.get(pid); + if (p != null) { + return p.mWakeSum + (p.mWakeStart != 0 ? (realtime - p.mWakeStart) : 0); + } + } + return 0; + } + + public void reportExcessiveWakeLocked(int uid, String proc, long overTime, long usedTime) { + Uid u = mUidStats.get(uid); + if (u != null) { + u.reportExcessiveWakeLocked(proc, overTime, usedTime); + } + } + + int mSensorNesting; + + public void noteStartSensorLocked(int uid, int sensor) { + if (mSensorNesting == 0) { + mHistoryCur.states |= HistoryItem.STATE_SENSOR_ON_FLAG; + if (DEBUG_HISTORY) Slog.v(TAG, "Start sensor to: " + + Integer.toHexString(mHistoryCur.states)); + addHistoryRecordLocked(SystemClock.elapsedRealtime()); + } + mSensorNesting++; + getUidStatsLocked(uid).noteStartSensor(sensor); + } + + public void noteStopSensorLocked(int uid, int sensor) { + mSensorNesting--; + if (mSensorNesting == 0) { + mHistoryCur.states &= ~HistoryItem.STATE_SENSOR_ON_FLAG; + if (DEBUG_HISTORY) Slog.v(TAG, "Stop sensor to: " + + Integer.toHexString(mHistoryCur.states)); + addHistoryRecordLocked(SystemClock.elapsedRealtime()); + } + getUidStatsLocked(uid).noteStopSensor(sensor); + } + int mGpsNesting; public void noteStartGpsLocked(int uid) { @@ -1244,6 +1350,10 @@ public final class BatteryStatsImpl extends BatteryStats { if (mScreenBrightnessBin >= 0) { mScreenBrightnessTimer[mScreenBrightnessBin].startRunningLocked(this); } + + // Fake a wake lock, so we consider the device waked as long + // as the screen is on. + noteStartWakeLocked(-1, -1, "dummy", 0); } } @@ -1258,6 +1368,8 @@ public final class BatteryStatsImpl extends BatteryStats { if (mScreenBrightnessBin >= 0) { mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this); } + + noteStopWakeLocked(-1, -1, "dummy", 0); } } @@ -1798,6 +1910,11 @@ public final class BatteryStatsImpl extends BatteryStats { */ final HashMap mPackageStats = new HashMap(); + /** + * The transient wake stats we have collected for this uid's pids. + */ + final SparseArray mPids = new SparseArray(); + public Uid(int uid) { mUid = uid; mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON, null, mUnpluggables); @@ -2081,27 +2198,27 @@ public final class BatteryStatsImpl extends BatteryStats { boolean active = false; if (mWifiTurnedOnTimer != null) { - active |= !mWifiTurnedOnTimer.reset(false); + active |= !mWifiTurnedOnTimer.reset(BatteryStatsImpl.this, false); active |= mWifiTurnedOn; } if (mFullWifiLockTimer != null) { - active |= !mFullWifiLockTimer.reset(false); + active |= !mFullWifiLockTimer.reset(BatteryStatsImpl.this, false); active |= mFullWifiLockOut; } if (mScanWifiLockTimer != null) { - active |= !mScanWifiLockTimer.reset(false); + active |= !mScanWifiLockTimer.reset(BatteryStatsImpl.this, false); active |= mScanWifiLockOut; } if (mWifiMulticastTimer != null) { - active |= !mWifiMulticastTimer.reset(false); + active |= !mWifiMulticastTimer.reset(BatteryStatsImpl.this, false); active |= mWifiMulticastEnabled; } if (mAudioTurnedOnTimer != null) { - active |= !mAudioTurnedOnTimer.reset(false); + active |= !mAudioTurnedOnTimer.reset(BatteryStatsImpl.this, false); active |= mAudioTurnedOn; } if (mVideoTurnedOnTimer != null) { - active |= !mVideoTurnedOnTimer.reset(false); + active |= !mVideoTurnedOnTimer.reset(BatteryStatsImpl.this, false); active |= mVideoTurnedOn; } @@ -2146,6 +2263,14 @@ public final class BatteryStatsImpl extends BatteryStats { } mProcessStats.clear(); } + if (mPids.size() > 0) { + for (int i=0; !active && i 0) { Iterator> it = mPackageStats.entrySet().iterator(); while (it.hasNext()) { @@ -2164,6 +2289,8 @@ public final class BatteryStatsImpl extends BatteryStats { mPackageStats.clear(); } + mPids.clear(); + if (!active) { if (mWifiTurnedOnTimer != null) { mWifiTurnedOnTimer.detach(); @@ -2412,13 +2539,13 @@ public final class BatteryStatsImpl extends BatteryStats { boolean reset() { boolean wlactive = false; if (mTimerFull != null) { - wlactive |= !mTimerFull.reset(false); + wlactive |= !mTimerFull.reset(BatteryStatsImpl.this, false); } if (mTimerPartial != null) { - wlactive |= !mTimerPartial.reset(false); + wlactive |= !mTimerPartial.reset(BatteryStatsImpl.this, false); } if (mTimerWindow != null) { - wlactive |= !mTimerWindow.reset(false); + wlactive |= !mTimerWindow.reset(BatteryStatsImpl.this, false); } if (!wlactive) { if (mTimerFull != null) { @@ -2486,7 +2613,7 @@ public final class BatteryStatsImpl extends BatteryStats { } boolean reset() { - if (mTimer.reset(true)) { + if (mTimer.reset(BatteryStatsImpl.this, true)) { mTimer = null; return true; } @@ -2598,6 +2725,8 @@ public final class BatteryStatsImpl extends BatteryStats { SamplingCounter[] mSpeedBins; + ArrayList mExcessiveWake; + Proc() { mUnpluggables.add(this); mSpeedBins = new SamplingCounter[getCpuSpeedSteps()]; @@ -2624,6 +2753,58 @@ public final class BatteryStatsImpl extends BatteryStats { } } + public int countExcessiveWakes() { + return mExcessiveWake != null ? mExcessiveWake.size() : 0; + } + + public ExcessiveWake getExcessiveWake(int i) { + if (mExcessiveWake != null) { + return mExcessiveWake.get(i); + } + return null; + } + + public void addExcessiveWake(long overTime, long usedTime) { + if (mExcessiveWake == null) { + mExcessiveWake = new ArrayList(); + } + ExcessiveWake ew = new ExcessiveWake(); + ew.overTime = overTime; + ew.usedTime = usedTime; + mExcessiveWake.add(ew); + } + + void writeExcessiveWakeToParcelLocked(Parcel out) { + if (mExcessiveWake == null) { + out.writeInt(0); + return; + } + + final int N = mExcessiveWake.size(); + out.writeInt(N); + for (int i=0; i(); + for (int i=0; i= 0) { + Pid p = getPidStatsLocked(pid); + p.mWakeStart = SystemClock.elapsedRealtime(); + } } - public void noteStopWakeLocked(String name, int type) { + public void noteStopWakeLocked(int pid, String name, int type) { StopwatchTimer t = getWakeTimerLocked(name, type); if (t != null) { t.stopRunningLocked(BatteryStatsImpl.this); } + if (pid >= 0) { + Pid p = mPids.get(pid); + if (p != null) { + p.mWakeSum += SystemClock.elapsedRealtime() - p.mWakeStart; + p.mWakeStart = 0; + } + } + } + + public void reportExcessiveWakeLocked(String proc, long overTime, long usedTime) { + Proc p = getProcessStatsLocked(proc); + if (p != null) { + p.addExcessiveWake(overTime, usedTime); + } } public void noteStartSensor(int sensor) { @@ -3372,6 +3589,10 @@ public final class BatteryStatsImpl extends BatteryStats { return mOnBattery; } + public boolean isScreenOn() { + return mScreenOn; + } + void initTimes() { mBatteryRealtime = mTrackBatteryPastUptime = 0; mBatteryUptime = mTrackBatteryPastRealtime = 0; @@ -3384,24 +3605,24 @@ public final class BatteryStatsImpl extends BatteryStats { public void resetAllStatsLocked() { mStartCount = 0; initTimes(); - mScreenOnTimer.reset(false); + mScreenOnTimer.reset(this, false); for (int i=0; i