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 --- .../com/android/server/PowerManagerService.java | 28 ++++--- .../com/android/server/WindowManagerService.java | 4 +- .../android/server/am/ActivityManagerService.java | 86 ++++++++++++++++++++++ .../com/android/server/am/BatteryStatsService.java | 12 +-- .../java/com/android/server/am/ProcessRecord.java | 7 +- .../java/com/android/server/am/ServiceRecord.java | 7 +- 6 files changed, 122 insertions(+), 22 deletions(-) (limited to 'services') diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 2fb481c..4ee89cc 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -151,6 +151,7 @@ class PowerManagerService extends IPowerManager.Stub static final int INITIAL_KEYBOARD_BRIGHTNESS = Power.BRIGHTNESS_OFF; private final int MY_UID; + private final int MY_PID; private boolean mDoneBooting = false; private boolean mBootCompleted = false; @@ -309,7 +310,7 @@ class PowerManagerService extends IPowerManager.Stub long ident = Binder.clearCallingIdentity(); try { PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken, - MY_UID, mTag); + MY_UID, MY_PID, mTag); mHeld = true; } finally { Binder.restoreCallingIdentity(ident); @@ -434,11 +435,11 @@ class PowerManagerService extends IPowerManager.Stub } } - PowerManagerService() - { + PowerManagerService() { // Hack to get our uid... should have a func for this. long token = Binder.clearCallingIdentity(); - MY_UID = Binder.getCallingUid(); + MY_UID = Process.myUid(); + MY_PID = Process.myPid(); Binder.restoreCallingIdentity(token); // XXX remove this when the kernel doesn't timeout wake locks @@ -573,13 +574,13 @@ class PowerManagerService extends IPowerManager.Stub private class WakeLock implements IBinder.DeathRecipient { - WakeLock(int f, IBinder b, String t, int u) { + WakeLock(int f, IBinder b, String t, int u, int p) { super(); flags = f; binder = b; tag = t; uid = u == MY_UID ? Process.SYSTEM_UID : u; - pid = Binder.getCallingPid(); + pid = p; if (u != MY_UID || ( !"KEEP_SCREEN_ON_FLAG".equals(tag) && !"KeyInputQueue".equals(tag))) { @@ -631,21 +632,23 @@ class PowerManagerService extends IPowerManager.Stub public void acquireWakeLock(int flags, IBinder lock, String tag) { int uid = Binder.getCallingUid(); + int pid = Binder.getCallingPid(); if (uid != Process.myUid()) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); } long ident = Binder.clearCallingIdentity(); try { synchronized (mLocks) { - acquireWakeLockLocked(flags, lock, uid, tag); + acquireWakeLockLocked(flags, lock, uid, pid, tag); } } finally { Binder.restoreCallingIdentity(ident); } } - public void acquireWakeLockLocked(int flags, IBinder lock, int uid, String tag) { + public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag) { int acquireUid = -1; + int acquirePid = -1; String acquireName = null; int acquireType = -1; @@ -657,7 +660,7 @@ class PowerManagerService extends IPowerManager.Stub WakeLock wl; boolean newlock; if (index < 0) { - wl = new WakeLock(flags, lock, tag, uid); + wl = new WakeLock(flags, lock, tag, uid, pid); switch (wl.flags & LOCK_MASK) { case PowerManager.FULL_WAKE_LOCK: @@ -730,13 +733,14 @@ class PowerManagerService extends IPowerManager.Stub } if (newlock) { acquireUid = wl.uid; + acquirePid = wl.pid; acquireName = wl.tag; acquireType = wl.monitorType; } if (acquireType >= 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 59deef3..e259887 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 dff6a8a..e5d1025 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; @@ -770,6 +773,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 37da6f7..7314e04 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 18b1acb..73e489f 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 75365ad..35f1875 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, -- cgit v1.1