diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index d66e98b3b3bbf..d5741fc4bc47e 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 a69938889f124..a0a3bdfd059d6 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= 0) { try { - mBatteryStats.noteStartWakelock(acquireUid, acquireName, acquireType); + mBatteryStats.noteStartWakelock(acquireUid, acquirePid, acquireName, acquireType); } catch (RemoteException e) { // Ignore } @@ -756,6 +760,7 @@ class PowerManagerService extends IPowerManager.Stub private void releaseWakeLockLocked(IBinder lock, int flags, boolean death) { int releaseUid; + int releasePid; String releaseName; int releaseType; @@ -800,13 +805,14 @@ class PowerManagerService extends IPowerManager.Stub // Unlink the lock from the binder. wl.binder.unlinkToDeath(wl, 0); releaseUid = wl.uid; + releasePid = wl.pid; releaseName = wl.tag; releaseType = wl.monitorType; if (releaseType >= 0) { long origId = Binder.clearCallingIdentity(); try { - mBatteryStats.noteStopWakelock(releaseUid, releaseName, releaseType); + mBatteryStats.noteStopWakelock(releaseUid, releasePid, releaseName, releaseType); } catch (RemoteException e) { // Ignore } finally { diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 59deef3eaefeb..e259887dc7597 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -8081,12 +8081,12 @@ public class WindowManagerService extends IWindowManager.Stub if (oldHold != newHold) { try { if (oldHold != null) { - mBatteryStats.noteStopWakelock(oldHold.mUid, + mBatteryStats.noteStopWakelock(oldHold.mUid, -1, "window", BatteryStats.WAKE_TYPE_WINDOW); } if (newHold != null) { - mBatteryStats.noteStartWakelock(newHold.mUid, + mBatteryStats.noteStartWakelock(newHold.mUid, -1, "window", BatteryStats.WAKE_TYPE_WINDOW); } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index dff6a8a9a7937..e5d102519f740 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -199,6 +199,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // The minimum amount of time between successive GC requests for a process. static final int GC_MIN_INTERVAL = 60*1000; + // The rate at which we check for apps using excessive wake locks -- 15 mins. + static final int WAKE_LOCK_CHECK_DELAY = 15*60*1000; + // How long we allow a receiver to run before giving up on it. static final int BROADCAST_TIMEOUT = 10*1000; @@ -769,6 +772,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen */ boolean mDidAppSwitch; + /** + * Last time (in realtime) at which we checked for wake lock usage. + */ + long mLastWakeLockCheckTime; + /** * Set while we are wanting to sleep, to prevent any * activities from being started/resumed. @@ -914,6 +922,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final int POST_HEAVY_NOTIFICATION_MSG = 24; static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25; static final int SHOW_STRICT_MODE_VIOLATION_MSG = 26; + static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27; AlertDialog mUidAlert; @@ -1173,6 +1182,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } catch (RemoteException e) { } } break; + case CHECK_EXCESSIVE_WAKE_LOCKS_MSG: { + synchronized (ActivityManagerService.this) { + checkExcessiveWakeLocksLocked(true); + mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG); + if (mSleeping) { + Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG); + mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY); + } + } + } break; } } }; @@ -2555,6 +2574,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mProcDeaths[0]++; + BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); + synchronized (stats) { + stats.noteProcessDiedLocked(app.info.uid, pid); + } + // Clean up already done if the process has been re-started. if (app.pid == pid && app.thread != null && app.thread.asBinder() == thread.asBinder()) { @@ -3570,6 +3594,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { + // Start looking for apps that are abusing wake locks. + Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG); + mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY); // Tell anyone interested that we are done booting! SystemProperties.set("sys.boot_completed", "1"); broadcastIntentLocked(null, null, @@ -5375,6 +5402,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } else { Slog.w(TAG, "goingToSleep with no resumed activity!"); } + + // Initialize the wake times of all processes. + checkExcessiveWakeLocksLocked(false); + mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG); + Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG); + mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY); } } @@ -5424,6 +5457,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mWindowManager.setEventDispatching(true); mSleeping = false; mMainStack.resumeTopActivityLocked(null); + mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG); } } @@ -11259,6 +11293,52 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + final void checkExcessiveWakeLocksLocked(boolean doKills) { + BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); + if (mLastWakeLockCheckTime == 0) { + doKills = false; + } + if (stats.isScreenOn()) { + doKills = false; + } + final long curRealtime = SystemClock.elapsedRealtime(); + final long timeSince = curRealtime - mLastWakeLockCheckTime; + mLastWakeLockCheckTime = curRealtime; + if (timeSince < 5*60*1000) { + doKills = false; + } + int i = mLruProcesses.size(); + while (i > 0) { + i--; + ProcessRecord app = mLruProcesses.get(i); + if (app.curAdj >= HIDDEN_APP_MIN_ADJ) { + long wtime; + synchronized (stats) { + wtime = stats.getProcessWakeTime(app.info.uid, + app.pid, curRealtime); + } + long timeUsed = wtime - app.lastWakeTime; + Slog.i(TAG, "Wake for " + app + ": over " + + timeSince + " used " + timeUsed + + " (" + ((timeUsed*100)/timeSince) + "%)"); + // If a process has held a wake lock for more + // than 50% of the time during this period, + // that sounds pad. Kill! + if (doKills && timeSince > 0 + && ((timeUsed*100)/timeSince) >= 50) { + Slog.i(TAG, "Excessive wake lock in " + app.processName + + " (pid " + app.pid + "): held " + timeUsed + + " during " + timeSince); + EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + app.processName, app.setAdj, "excessive wake lock"); + Process.killProcessQuiet(app.pid); + } else { + app.lastWakeTime = wtime; + } + } + } + } + private final boolean updateOomAdjLocked( ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) { app.hiddenAdj = hiddenAdj; @@ -11281,6 +11361,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Likewise do a gc when an app is moving in to the // background (such as a service stopping). scheduleAppGcLocked(app); + // And note its current wake lock time. + BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); + synchronized (stats) { + app.lastWakeTime = stats.getProcessWakeTime(app.info.uid, + app.pid, SystemClock.elapsedRealtime()); + } } app.setRawAdj = app.curRawAdj; } diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java index 37da6f7764e06..7314e041cdcd8 100644 --- a/services/java/com/android/server/am/BatteryStatsService.java +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -93,31 +93,31 @@ public final class BatteryStatsService extends IBatteryStats.Stub { return data; } - public void noteStartWakelock(int uid, String name, int type) { + public void noteStartWakelock(int uid, int pid, String name, int type) { enforceCallingPermission(); synchronized (mStats) { - mStats.getUidStatsLocked(uid).noteStartWakeLocked(name, type); + mStats.noteStartWakeLocked(uid, pid, name, type); } } - public void noteStopWakelock(int uid, String name, int type) { + public void noteStopWakelock(int uid, int pid, String name, int type) { enforceCallingPermission(); synchronized (mStats) { - mStats.getUidStatsLocked(uid).noteStopWakeLocked(name, type); + mStats.noteStopWakeLocked(uid, pid, name, type); } } public void noteStartSensor(int uid, int sensor) { enforceCallingPermission(); synchronized (mStats) { - mStats.getUidStatsLocked(uid).noteStartSensor(sensor); + mStats.noteStartSensorLocked(uid, sensor); } } public void noteStopSensor(int uid, int sensor) { enforceCallingPermission(); synchronized (mStats) { - mStats.getUidStatsLocked(uid).noteStopSensor(sensor); + mStats.noteStopSensorLocked(uid, sensor); } } diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 18b1acbf44846..73e489f9e9bac 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -74,6 +74,7 @@ class ProcessRecord { Bundle instrumentationArguments;// as given to us ComponentName instrumentationResultClass;// copy of instrumentationClass BroadcastRecord curReceiver;// receiver currently running in the app + long lastWakeTime; // How long proc held wake lock at last check long lastRequestedGc; // When we last asked the app to do a gc long lastLowMemory; // When we last told the app that memory is low boolean reportLowMemory; // Set to true when waiting to report low mem @@ -158,7 +159,7 @@ class ProcessRecord { pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting="); pw.print(starting); pw.print(" lastPss="); pw.println(lastPss); pw.print(prefix); pw.print("lastActivityTime="); pw.print(lastActivityTime); - pw.print(" lruWeight="); pw.println(lruWeight); + pw.print(" lruWeight="); pw.print(lruWeight); pw.print(" hidden="); pw.print(hidden); pw.print(" empty="); pw.println(empty); pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj); @@ -177,6 +178,10 @@ class ProcessRecord { pw.print(" persistentActivities="); pw.println(persistentActivities); pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq); pw.print(" lruSeq="); pw.println(lruSeq); + pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime); + pw.print(" lastRequestedGc="); pw.print(lastRequestedGc); + pw.print(" lastLowMemory="); pw.print(lastLowMemory); + pw.print(" reportLowMemory="); pw.println(reportLowMemory); if (killedBackground) { pw.print(prefix); pw.print("killedBackground="); pw.println(killedBackground); } diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java index 75365ad90c8d2..35f187509cabf 100644 --- a/services/java/com/android/server/am/ServiceRecord.java +++ b/services/java/com/android/server/am/ServiceRecord.java @@ -149,7 +149,9 @@ class ServiceRecord extends Binder { pw.print(" foregroundId="); pw.print(foregroundId); pw.print(" foregroundNoti="); pw.println(foregroundNoti); } - pw.print(prefix); pw.print("lastActivity="); pw.print(lastActivity-now); + pw.print(prefix); pw.print("createTime="); + pw.print(createTime-SystemClock.elapsedRealtime()); + pw.print(" lastActivity="); pw.print(lastActivity-now); pw.print(" executingStart="); pw.print(executingStart-now); pw.print(" restartTime="); pw.println(restartTime); if (startRequested || lastStartId != 0) { @@ -213,7 +215,8 @@ class ServiceRecord extends Binder { dataDir = sInfo.applicationInfo.dataDir; exported = sInfo.exported; this.restarter = restarter; - createTime = lastActivity = SystemClock.uptimeMillis(); + createTime = SystemClock.elapsedRealtime(); + lastActivity = SystemClock.uptimeMillis(); } public AppBindRecord retrieveAppBindingLocked(Intent intent, diff --git a/tests/BatteryWaster/res/layout/battery_waster.xml b/tests/BatteryWaster/res/layout/battery_waster.xml index e1cb6bff1c68b..36aa68b88706c 100644 --- a/tests/BatteryWaster/res/layout/battery_waster.xml +++ b/tests/BatteryWaster/res/layout/battery_waster.xml @@ -30,6 +30,16 @@ android:text="@string/waste_away" /> + + Discharge my battery! + Keep my device awake! + diff --git a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java index 8ea7e0036543b..499330fca4d8e 100644 --- a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java +++ b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java @@ -41,6 +41,8 @@ public class BatteryWaster extends Activity { PowerManager.WakeLock mWakeLock; SpinThread mThread; + boolean mWasting, mWaking; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -50,6 +52,7 @@ public class BatteryWaster extends Activity { setContentView(R.layout.battery_waster); findViewById(R.id.checkbox).setOnClickListener(mClickListener); + findViewById(R.id.checkbox_wake).setOnClickListener(mWakeClickListener); mLog = (TextView)findViewById(R.id.log); mDateFormat = DateFormat.getInstance(); @@ -67,9 +70,18 @@ public class BatteryWaster extends Activity { @Override public void onPause() { + super.onPause(); stopRunning(); } + @Override + public void onDestroy() { + super.onDestroy(); + if (mWakeLock.isHeld()) { + mWakeLock.release(); + } + } + View.OnClickListener mClickListener = new View.OnClickListener() { public void onClick(View v) { CheckBox checkbox = (CheckBox)v; @@ -81,23 +93,54 @@ public class BatteryWaster extends Activity { } }; + View.OnClickListener mWakeClickListener = new View.OnClickListener() { + public void onClick(View v) { + CheckBox checkbox = (CheckBox)v; + if (checkbox.isChecked()) { + mWaking = true; + updateWakeLock(); + } else { + mWaking = false; + updateWakeLock(); + } + } + }; + void startRunning() { - log("Start"); - registerReceiver(mReceiver, mFilter); - mWakeLock.acquire(); - if (mThread == null) { - mThread = new SpinThread(); - mThread.start(); + if (!mWasting) { + log("Start"); + registerReceiver(mReceiver, mFilter); + mWasting = true; + updateWakeLock(); + if (mThread == null) { + mThread = new SpinThread(); + mThread.start(); + } } } void stopRunning() { - log("Stop"); - unregisterReceiver(mReceiver); - mWakeLock.release(); - if (mThread != null) { - mThread.quit(); - mThread = null; + if (mWasting) { + log("Stop"); + unregisterReceiver(mReceiver); + mWasting = false; + updateWakeLock(); + if (mThread != null) { + mThread.quit(); + mThread = null; + } + } + } + + void updateWakeLock() { + if (mWasting || mWaking) { + if (!mWakeLock.isHeld()) { + mWakeLock.acquire(); + } + } else { + if (mWakeLock.isHeld()) { + mWakeLock.release(); + } } }